@Override
    public boolean process(byte[] input, int offset, int len, boolean finish) {
      if (this.state == 6) {
        return false;
      }

      int p = offset;
      len += offset;

      // Using local variables makes the decoder about 12%
      // faster than if we manipulate the member variables in
      // the loop.  (Even alphabet makes a measurable
      // difference, which is somewhat surprising to me since
      // the member variable is final.)
      int state = this.state;
      int value = this.value;
      int op = 0;
      final byte[] output = this.output;
      final int[] alphabet = this.alphabet;

      while (p < len) {
        if (state == 0) {
          while (p + 4 <= len
              && (value =
                      ((alphabet[input[p] & 0xff] << 18)
                          | (alphabet[input[p + 1] & 0xff] << 12)
                          | (alphabet[input[p + 2] & 0xff] << 6)
                          | (alphabet[input[p + 3] & 0xff])))
                  >= 0) {
            output[op + 2] = (byte) value;
            output[op + 1] = (byte) (value >> 8);
            output[op] = (byte) (value >> 16);
            op += 3;
            p += 4;
          }
          if (p >= len) {
            break;
          }
        }

        // The fast path isn't available -- either we've read a
        // partial tuple, or the next four input bytes aren't all
        // data, or whatever.  Fall back to the slower state
        // machine implementation.

        int d = alphabet[input[p++] & 0xff];

        switch (state) {
          case 0:
            if (d >= 0) {
              value = d;
              ++state;
            } else if (d != SKIP) {
              this.state = 6;
              return false;
            }
            break;

          case 1:
            if (d >= 0) {
              value = (value << 6) | d;
              ++state;
            } else if (d != SKIP) {
              this.state = 6;
              return false;
            }
            break;

          case 2:
            if (d >= 0) {
              value = (value << 6) | d;
              ++state;
            } else if (d == EQUALS) {
              // Emit the last (partial) output tuple;
              // expect exactly one more padding character.
              output[op++] = (byte) (value >> 4);
              state = 4;
            } else if (d != SKIP) {
              this.state = 6;
              return false;
            }
            break;

          case 3:
            if (d >= 0) {
              // Emit the output triple and return to state 0.
              value = (value << 6) | d;
              output[op + 2] = (byte) value;
              output[op + 1] = (byte) (value >> 8);
              output[op] = (byte) (value >> 16);
              op += 3;
              state = 0;
            } else if (d == EQUALS) {
              // Emit the last (partial) output tuple;
              // expect no further data or padding characters.
              output[op + 1] = (byte) (value >> 2);
              output[op] = (byte) (value >> 10);
              op += 2;
              state = 5;
            } else if (d != SKIP) {
              this.state = 6;
              return false;
            }
            break;

          case 4:
            if (d == EQUALS) {
              ++state;
            } else if (d != SKIP) {
              this.state = 6;
              return false;
            }
            break;

          case 5:
            if (d != SKIP) {
              this.state = 6;
              return false;
            }
            break;
          default:
            break;
        }
      }

      if (!finish) {
        // We're out of input, but a future call could provide
        // more.
        this.state = state;
        this.value = value;
        this.op = op;
        return true;
      }

      // Done reading input.  Now figure out where we are left in
      // the state machine and finish up.

      switch (state) {
        case 0:
          // Output length is a multiple of three.  Fine.
          break;
        case 1:
          // Read one extra input byte, which isn't enough to
          // make another output byte.  Illegal.
          this.state = 6;
          return false;
        case 2:
          // Read two extra input bytes, enough to emit 1 more
          // output byte.  Fine.
          output[op++] = (byte) (value >> 4);
          break;
        case 3:
          // Read three extra input bytes, enough to emit 2 more
          // output bytes.  Fine.
          output[op++] = (byte) (value >> 10);
          output[op++] = (byte) (value >> 2);
          break;
        case 4:
          // Read one padding '=' when we expected 2.  Illegal.
          this.state = 6;
          return false;
        case 5:
          // Read all the padding '='s we expected and no more.
          // Fine.
          break;
        default:
          break;
      }

      this.state = state;
      this.op = op;
      return true;
    }
Decode another block of input data.

@return true if the state machine is still healthy. false if bad base-64 data has been
    detected in the input stream.
java_src/src/main/java/com/google/crypto/tink/subtle/Base64.java