001/******************************************************************************* 002The MIT License (MIT) 003 004Copyright (c) 2024 KILLCODING.COM 005 006Permission is hereby granted, free of charge, to any person obtaining a copy 007of this software and associated documentation files (the "Software"), to deal 008in the Software without restriction, including without limitation the rights 009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010copies of the Software, and to permit persons to whom the Software is 011furnished to do so, subject to the following conditions: 012 013The above copyright notice and this permission notice shall be included in 014all copies or substantial portions of the Software. 015 016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 022THE SOFTWARE. 023*****************************************************************************/ 024package com.killcoding.tool; 025 026import com.killcoding.file.BaseFile; 027import java.io.UnsupportedEncodingException; 028 029/** 030 * This class is support Base64 tools 031 * */ 032public final class Base64Tools { 033 034 // Mapping table from 6-bit nibbles to Base64 characters. 035 private static final char[] map1 = new char[64]; 036 static { 037 int i = 0; 038 for (char c = 'A'; c <= 'Z'; c++) 039 map1[i++] = c; 040 for (char c = 'a'; c <= 'z'; c++) 041 map1[i++] = c; 042 for (char c = '0'; c <= '9'; c++) 043 map1[i++] = c; 044 map1[i++] = '+'; 045 map1[i++] = '/'; 046 } 047 048 // Mapping table from Base64 characters to 6-bit nibbles. 049 private static final byte[] map2 = new byte[128]; 050 static { 051 for (int i = 0; i < map2.length; i++) 052 map2[i] = -1; 053 for (int i = 0; i < 64; i++) 054 map2[map1[i]] = (byte) i; 055 } 056 057 public static String encodeString(String s) throws UnsupportedEncodingException { 058 return new String(encode(s.getBytes(BaseFile.CHARSET))); 059 } 060 061 public static String encodeLines(byte[] in) { 062 return encodeLines(in, 0, in.length, 76, System.lineSeparator()); 063 } 064 065 public static String encodeLines(byte[] in, int iOff, int iLen, 066 int lineLen, String lineSeparator) { 067 int blockLen = (lineLen * 3) / 4; 068 if (blockLen <= 0) 069 throw new IllegalArgumentException(); 070 int lines = (iLen + blockLen - 1) / blockLen; 071 int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); 072 StringBuilder buf = new StringBuilder(bufLen); 073 int ip = 0; 074 while (ip < iLen) { 075 int l = Math.min(iLen - ip, blockLen); 076 buf.append(encode(in, iOff + ip, l)); 077 buf.append(lineSeparator); 078 ip += l; 079 } 080 return buf.toString(); 081 } 082 083 public static char[] encode(byte[] in) { 084 return encode(in, 0, in.length); 085 } 086 087 public static char[] encode(byte[] in, int iLen) { 088 return encode(in, 0, iLen); 089 } 090 091 public static char[] encode(byte[] in, int iOff, int iLen) { 092 int oDataLen = (iLen * 4 + 2) / 3; // output length without padding 093 int oLen = ((iLen + 2) / 3) * 4; // output length including padding 094 char[] out = new char[oLen]; 095 int ip = iOff; 096 int iEnd = iOff + iLen; 097 int op = 0; 098 while (ip < iEnd) { 099 int i0 = in[ip++] & 0xff; 100 int i1 = ip < iEnd ? in[ip++] & 0xff : 0; 101 int i2 = ip < iEnd ? in[ip++] & 0xff : 0; 102 int o0 = i0 >>> 2; 103 int o1 = ((i0 & 3) << 4) | (i1 >>> 4); 104 int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); 105 int o3 = i2 & 0x3F; 106 out[op++] = map1[o0]; 107 out[op++] = map1[o1]; 108 out[op] = op < oDataLen ? map1[o2] : '='; 109 op++; 110 out[op] = op < oDataLen ? map1[o3] : '='; 111 op++; 112 } 113 return out; 114 } 115 116 public static String decodeString(String s) throws UnsupportedEncodingException { 117 return new String(decode(s),BaseFile.CHARSET); 118 } 119 120 public static byte[] decodeLines(String s) { 121 char[] buf = new char[s.length()]; 122 int p = 0; 123 for (int ip = 0; ip < s.length(); ip++) { 124 char c = s.charAt(ip); 125 if (c != ' ' && c != '\r' && c != '\n' && c != '\t') 126 buf[p++] = c; 127 } 128 return decode(buf, 0, p); 129 } 130 131 public static byte[] decode(String s) { 132 return decode(s.toCharArray()); 133 } 134 135 public static byte[] decode(char[] in) { 136 return decode(in, 0, in.length); 137 } 138 139 public static byte[] decode(char[] in, int iOff, int iLen) { 140 if (iLen % 4 != 0) 141 throw new IllegalArgumentException( 142 "Length of Base64 encoded input string is not a multiple of 4."); 143 while (iLen > 0 && in[iOff + iLen - 1] == '=') 144 iLen--; 145 int oLen = (iLen * 3) / 4; 146 byte[] out = new byte[oLen]; 147 int ip = iOff; 148 int iEnd = iOff + iLen; 149 int op = 0; 150 while (ip < iEnd) { 151 int i0 = in[ip++]; 152 int i1 = in[ip++]; 153 int i2 = ip < iEnd ? in[ip++] : 'A'; 154 int i3 = ip < iEnd ? in[ip++] : 'A'; 155 if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) 156 throw new IllegalArgumentException( 157 "Illegal character in Base64 encoded data."); 158 int b0 = map2[i0]; 159 int b1 = map2[i1]; 160 int b2 = map2[i2]; 161 int b3 = map2[i3]; 162 if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) 163 throw new IllegalArgumentException( 164 "Illegal character in Base64 encoded data."); 165 int o0 = (b0 << 2) | (b1 >>> 4); 166 int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); 167 int o2 = ((b2 & 3) << 6) | b3; 168 out[op++] = (byte) o0; 169 if (op < oLen) 170 out[op++] = (byte) o1; 171 if (op < oLen) 172 out[op++] = (byte) o2; 173 } 174 return out; 175 } 176 177}