/**
  * @param bindex Relative index within base64 character unit; between 0 and 3 (as unit has exactly
  *     4 characters)
  */
 protected IllegalArgumentException reportInvalidBase64Char(
     Base64Variant b64variant, int ch, int bindex, String msg) throws IllegalArgumentException {
   String base;
   if (ch <= INT_SPACE) {
     base =
         "Illegal white space character (code 0x"
             + Integer.toHexString(ch)
             + ") as character #"
             + (bindex + 1)
             + " of 4-char base64 unit: can only used between units";
   } else if (b64variant.usesPaddingChar(ch)) {
     base =
         "Unexpected padding character ('"
             + b64variant.getPaddingChar()
             + "') as character #"
             + (bindex + 1)
             + " of 4-char base64 unit: padding only legal as 3rd or 4th character";
   } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
     // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
     base = "Illegal character (code 0x" + Integer.toHexString(ch) + ") in base64 content";
   } else {
     base =
         "Illegal character '"
             + ((char) ch)
             + "' (code 0x"
             + Integer.toHexString(ch)
             + ") in base64 content";
   }
   if (msg != null) {
     base = base + ": " + msg;
   }
   return new IllegalArgumentException(base);
 }
  protected void _writeBinary(
      Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)
      throws IOException, JsonGenerationException {
    // Encoding is by chunks of 3 input, 4 output chars, so:
    int safeInputEnd = inputEnd - 3;
    // Let's also reserve room for possible (and quoted) lf char each round
    int safeOutputEnd = _outputEnd - 6;
    int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;

    // Ok, first we loop through all full triplets of data:
    while (inputPtr <= safeInputEnd) {
      if (_outputTail > safeOutputEnd) { // need to flush
        _flushBuffer();
      }
      // First, mash 3 bytes into lsb of 32-bit int
      int b24 = ((int) input[inputPtr++]) << 8;
      b24 |= ((int) input[inputPtr++]) & 0xFF;
      b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
      _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
      if (--chunksBeforeLF <= 0) {
        // note: must quote in JSON value
        _outputBuffer[_outputTail++] = '\\';
        _outputBuffer[_outputTail++] = 'n';
        chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
      }
    }

    // And then we may have 1 or 2 leftover bytes to encode
    int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
    if (inputLeft > 0) { // yes, but do we have room for output?
      if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
        _flushBuffer();
      }
      int b24 = ((int) input[inputPtr++]) << 16;
      if (inputLeft == 2) {
        b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
      }
      _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
    }
  }
 protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index)
     throws IOException, JsonParseException {
   // 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
   if (ch != '\\') {
     throw reportInvalidBase64Char(b64variant, ch, index);
   }
   char unescaped = _decodeEscaped();
   // if white space, skip if first triplet; otherwise errors
   if (unescaped <= INT_SPACE) {
     if (index == 0) { // whitespace only allowed to be skipped between triplets
       return -1;
     }
   }
   // otherwise try to find actual triplet value
   int bits = b64variant.decodeBase64Char(unescaped);
   if (bits < 0) {
     throw reportInvalidBase64Char(b64variant, unescaped, index);
   }
   return bits;
 }