001/** 002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC 003 * and GZip files. (http://jwat.org/) 004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/) 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.jwat.common; 019 020import java.io.ByteArrayOutputStream; 021import java.util.Arrays; 022 023/** 024 * Base16 encoder/decoder implementation based on the specifications in 025 * rfc3548. 026 * 027 * @author nicl 028 */ 029public class Base16 { 030 031 /** Ascii table used to encode. */ 032 public static String encodeTab = "0123456789ABCDEF"; 033 034 /** Table used to decode. */ 035 public static byte[] decodeTab = new byte[ 256 ]; 036 037 /** Populate decode table. */ 038 static { 039 Arrays.fill( decodeTab, (byte)0xff ); 040 for ( int i=0; i<encodeTab.length(); ++i ) { 041 decodeTab[ Character.toUpperCase( encodeTab.charAt( i ) ) ] = (byte)i; 042 decodeTab[ Character.toLowerCase( encodeTab.charAt( i ) ) ] = (byte)i; 043 } 044 } 045 046 /** 047 * Static class. 048 */ 049 protected Base16() { 050 } 051 052 /** 053 * Decodes an encoded string. 054 * @param in encoded string. 055 * @return decoded string or null 056 */ 057 public static String decodeToString(String in) { 058 if (in == null) { 059 return null; 060 } 061 if (in.length() == 0) { 062 return ""; 063 } 064 065 StringBuffer out = new StringBuffer( 256 ); 066 067 boolean b = true; 068 int idx = 0; 069 char cin; 070 int mod = 0; 071 int cIdx; 072 int cout = 0; 073 074 /* 075 * Loop. 076 */ 077 078 while ( b ) { 079 if ( idx < in.length() ) { 080 cin = in.charAt( idx++ ); 081 cIdx = decodeTab[ cin ]; 082 if ( cIdx != -1 ) { 083 switch ( mod ) { 084 case 0: 085 cout = cIdx << 4; 086 break; 087 case 1: 088 cout |= cIdx; 089 out.append( (char)cout ); 090 break; 091 } 092 mod = ( mod + 1 ) % 2; 093 } 094 else { 095 return null; 096 } 097 } 098 else { 099 b = false; 100 } 101 } 102 103 /* 104 * Padding. 105 */ 106 107 if ( mod != 1 ) { 108 return out.toString(); 109 } 110 else { 111 // In state 1 only if decoded a half byte. 112 return null; 113 } 114 } 115 116 /** 117 * Decodes an encoded string. 118 * @param in encoded string. 119 * @return decoded byte array or null 120 */ 121 public static byte[] decodeToArray(String in) { 122 if (in == null) { 123 return null; 124 } 125 if (in.length() == 0) { 126 return new byte[0]; 127 } 128 129 ByteArrayOutputStream out = new ByteArrayOutputStream(); 130 131 boolean b = true; 132 int idx = 0; 133 char cin; 134 int mod = 0; 135 int cIdx; 136 int cout = 0; 137 138 /* 139 * Loop. 140 */ 141 142 while ( b ) { 143 if ( idx < in.length() ) { 144 cin = in.charAt( idx++ ); 145 cIdx = decodeTab[ cin ]; 146 if ( cIdx != -1 ) { 147 switch ( mod ) { 148 case 0: 149 cout = cIdx << 4; 150 break; 151 case 1: 152 cout |= cIdx; 153 out.write( cout ); 154 break; 155 } 156 mod = ( mod + 1 ) % 2; 157 } 158 else { 159 return null; 160 } 161 } 162 else { 163 b = false; 164 } 165 } 166 167 /* 168 * Padding. 169 */ 170 171 if ( mod != 1 ) { 172 return out.toByteArray(); 173 } 174 else { 175 // In state 1 only if decoded a half byte. 176 return null; 177 } 178 } 179 180 /** 181 * Encodes a string. 182 * @param in unencoded text string. 183 * @return encoded string or null 184 */ 185 public static String encodeString(String in) { 186 if (in == null) { 187 return null; 188 } 189 if (in.length() == 0) { 190 return ""; 191 } 192 193 StringBuffer out = new StringBuffer( 256 ); 194 195 boolean b = true; 196 int idx = 0; 197 int cin; 198 199 /* 200 * Loop. 201 */ 202 203 while ( b ) { 204 if ( idx < in.length() ) { 205 cin = in.charAt( idx++ ); 206 if ( cin < 256 ) { 207 out.append( encodeTab.charAt( (cin >> 4) & 15 ) ); 208 out.append( encodeTab.charAt( cin & 15 ) ); 209 } 210 else { 211 return null; 212 } 213 } 214 else { 215 b = false; 216 } 217 } 218 219 return out.toString(); 220 } 221 222 /** 223 * Encodes a byte array. 224 * @param in byte array. 225 * @return encoded string or null 226 */ 227 public static String encodeArray(byte[] in) { 228 if (in == null) { 229 return null; 230 } 231 if (in.length == 0) { 232 return ""; 233 } 234 235 StringBuffer out = new StringBuffer( 256 ); 236 237 boolean b = true; 238 int idx = 0; 239 int cin; 240 241 /* 242 * Loop. 243 */ 244 245 while ( b ) { 246 if ( idx < in.length ) { 247 cin = in[ idx++ ] & 255; 248 out.append( encodeTab.charAt( (cin >> 4) & 15 ) ); 249 out.append( encodeTab.charAt( cin & 15 ) ); 250 } 251 else { 252 b = false; 253 } 254 } 255 256 return out.toString(); 257 } 258 259}