001/* 002 * ============================================================================ 003 * Copyright © 2002-2023 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.util; 019 020import static java.math.BigInteger.ZERO; 021import static java.util.Arrays.fill; 022import static java.util.Locale.ROOT; 023import static org.apiguardian.api.API.Status.STABLE; 024import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_STRING; 025import static org.tquadrat.foundation.lang.CommonConstants.UTF8; 026import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 027 028import java.math.BigInteger; 029 030import org.apiguardian.api.API; 031import org.tquadrat.foundation.annotation.ClassVersion; 032import org.tquadrat.foundation.annotation.UtilityClass; 033import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 034import org.tquadrat.foundation.exception.ValidationException; 035 036/** 037 * <p>{@summary This class provides an Encoder and a Decoder for 038 * {@href https://www.crockford.com/base32.html Crockfords's Base 32} 039 * format.}</p> 040 * <p>Base 32 allows to represent large numbers as strings with less 041 * characters than Base 10 (the regular decimal system) or Base 16 042 * (the hexadecimal system). It has the advantage over Base 64 that it 043 * uses less symbols, and no special characters.</p> 044 * <p>While Base is mainly for (large) numbers, it can be used for 045 * strings, to, in the same way as Base 64. But different from that, 046 * Base 32 is not really standardized; this version, introduced by 047 * Douglas Crockford, is just one among various others.</p> 048 * 049 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 050 * @thanks Douglas Crockford - douglas@crockford.com 051 * @version $Id: Base32.java 1060 2023-09-24 19:21:40Z tquadrat $ 052 * @since 0.1.0 053 * 054 * @UMLGraph.link 055 */ 056@SuppressWarnings( {"JavadocLinkAsPlainText", "MagicNumber"} ) 057@ClassVersion( sourceVersion = "$Id: Base32.java 1060 2023-09-24 19:21:40Z tquadrat $" ) 058@API( status = STABLE, since = "0.1.0" ) 059@UtilityClass 060public final class Base32 061{ 062 /*---------------*\ 063 ====** Inner Classes **==================================================== 064 \*---------------*/ 065 /** 066 * <p>{@summary The Decoder for 067 * {@href https://www.crockford.com/base32.html Crockfords's Base 32} 068 * format.}</p> 069 * 070 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 071 * @thanks Douglas Crockford - douglas@crockford.com 072 * @version $Id: Base32.java 1060 2023-09-24 19:21:40Z tquadrat $ 073 * @since 0.1.0 074 * 075 * @UMLGraph.link 076 */ 077 @ClassVersion( sourceVersion = "$Id: Base32.java 1060 2023-09-24 19:21:40Z tquadrat $" ) 078 @API( status = STABLE, since = "0.1.0" ) 079 public static final class Decoder 080 { 081 /*--------------*\ 082 ====** Constructors **================================================= 083 \*--------------*/ 084 /** 085 * Creates a new instance of {@code Base32.Decoder}. 086 */ 087 private Decoder() { /* Just exists */ } 088 089 /*---------*\ 090 ====** Methods **====================================================== 091 \*---------*/ 092 /** 093 * Checks whether the given argument is a valid Basic 32 094 * sequence and converts it into an array with the symbol values. 095 * 096 * @param src The data to check. 097 * @return The symbol values. 098 * @throws IllegalArgumentException The input data is invalid. 099 */ 100 private final long [] checkValid( final byte [] src ) 101 { 102 requireNonNullArgument( src, "src" ); 103 final var retValue = new long [src.length]; 104 for( var i = 0; i < src.length; ++i ) 105 { 106 final var value = ALPHABET_VALUES [(char) src [i]]; 107 if( value == -1 ) throw new ValidationException( "No valid Base32 sequence" ); 108 retValue [i] = value; 109 } 110 111 //---* Done *------------------------------------------------------ 112 return retValue; 113 } // checkValid() 114 115 /** 116 * <p>{@summary Decodes an array of Base 32 symbols.}</p> 117 * <p>This is the converse operation to 118 * {@link Encoder#encode(byte[])}</p> 119 * 120 * @param src The input data. 121 * @return The decoded value. 122 * @throws ValidationException The input data is not a valid 123 * Base 32 sequence 124 */ 125 public final byte [] decode( final byte [] src ) throws ValidationException 126 { 127 var retValue = EMPTY_byte_ARRAY; 128 if( requireNonNullArgument( src, "src" ).length > 0 ) 129 { 130 final var buffer = decodeToNumber( src ); 131 retValue = buffer.toByteArray(); 132 } 133 134 //---* Done *------------------------------------------------------ 135 return retValue; 136 } // decode() 137 138 /** 139 * <p>{@summary Decodes a Base 32 string.}</p> 140 * <p>This is the converse operation to 141 * {@link Encoder#encodeToString(byte[])}.</p> 142 * 143 * @param src The input data. 144 * @return The decoded value. 145 * @throws ValidationException The input data is not a valid 146 * Base 32 sequence 147 */ 148 public final byte [] decode( final String src ) throws ValidationException 149 { 150 final var buffer = decodeToNumber( requireNonNullArgument( src, "src" ).getBytes( UTF8) ); 151 final var retValue = buffer.toByteArray(); 152 153 //---* Done *------------------------------------------------------ 154 return retValue; 155 } // decode() 156 157 /** 158 * Decodes an array of Base 32 symbols to a number. 159 * 160 * @param src The input data. 161 * @return The decoded value. 162 * @throws ValidationException The input data is not a valid 163 * Base 32 sequence 164 */ 165 public final BigInteger decodeToNumber( final byte [] src ) throws ValidationException 166 { 167 final var sourceValues = checkValid( src ); 168 var shift = (sourceValues.length - 1) * 5; 169 var retValue = ZERO; 170 for( final var value : sourceValues ) 171 { 172 retValue = retValue.or( BigInteger.valueOf( value ).shiftLeft( shift ) ); 173 shift -= 5; 174 } 175 176 //---* Done *------------------------------------------------------ 177 return retValue; 178 } // decodeToNumber() 179 180 /** 181 * <p>{@summary Decodes a Base string to a number.}</p> 182 * <p>This is the converse operation to 183 * {@link Encoder#encodeToString(BigInteger)}.</p> 184 * 185 * @param src The input data. 186 * @return The decoded value. 187 * @throws ValidationException The input data is not a valid 188 * Base 32 sequence 189 */ 190 public final BigInteger decodeToNumber( final String src ) throws ValidationException 191 { 192 final var retValue = decodeToNumber( requireNonNullArgument( src, "src" ).getBytes( UTF8 ) ); 193 194 //---* Done *------------------------------------------------------ 195 return retValue; 196 } // decodeToNumber() 197 198 /** 199 * <p>{@summary Decodes an array of Base 32 symbols to a 200 * String.}</p> 201 * <p>This is the converse operation to 202 * {@link Encoder#encode(String)}.</p> 203 * 204 * @param src The input data. 205 * @return The decoded value. 206 * @throws ValidationException The input data is not a valid 207 * Base 32 sequence 208 */ 209 public final String decodeToString( final byte [] src ) throws ValidationException 210 { 211 final var buffer = decode( src ); 212 final var retValue = new String( buffer, UTF8 ); 213 214 //---* Done *------------------------------------------------------ 215 return retValue; 216 } // decodeToString() 217 218 /** 219 * <p>{@summary Decodes a Base string to a String.}</p> 220 * <p>This is the converse operation to 221 * {@link Encoder#encodeToString(String)}.</p> 222 * 223 * @param src The input data. 224 * @return The decoded value. 225 * @throws ValidationException The input data is not a valid 226 * Base 32 sequence 227 */ 228 public final String decodeToString( final String src ) throws ValidationException 229 { 230 var retValue = EMPTY_STRING; 231 if( !requireNonNullArgument( src, "src" ).isEmpty() ) retValue = decodeToString( src.getBytes( UTF8 ) ); 232 233 //---* Done *------------------------------------------------------ 234 return retValue; 235 } // decodeToString() 236 } 237 // class Decoder 238 239 /** 240 * <p>{@summary The Encoder for 241 * {@href https://www.crockford.com/base32.html Crockfords's Base 32} 242 * format.}</p> 243 * 244 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 245 * @thanks Douglas Crockford - douglas@crockford.com 246 * @version $Id: Base32.java 1060 2023-09-24 19:21:40Z tquadrat $ 247 * @since 0.1.0 248 * 249 * @UMLGraph.link 250 */ 251 @ClassVersion( sourceVersion = "$Id: Base32.java 1060 2023-09-24 19:21:40Z tquadrat $" ) 252 @API( status = STABLE, since = "0.1.0" ) 253 public static final class Encoder 254 { 255 /*--------------*\ 256 ====** Constructors **================================================= 257 \*--------------*/ 258 /** 259 * Creates a new instance of {@code Base32.Encoder}. 260 */ 261 private Encoder() { /* Just exists */ } 262 263 /*---------*\ 264 ====** Methods **====================================================== 265 \*---------*/ 266 /** 267 * <p>{@summary Encodes the given value to Crockford's 268 * Base 32.}</p> 269 * <p>This is the converse operation to 270 * {@link Decoder#decode(byte[])}.</p> 271 * 272 * @param value The value to encode. It must be a positive number. 273 * @return The result. 274 */ 275 public final byte [] encode( final BigInteger value ) 276 { 277 if( requireNonNullArgument( value, "value" ).signum() < 0 ) 278 { 279 throw new ValidationException( "%d is negative".formatted( value ) ); 280 } 281 final var buffer = value.toString( RADIX ) 282 .toUpperCase( ROOT ); 283 final var len = buffer.length(); 284 final var retValue = new byte[ len ]; 285 for( var i = 0; i < len; ++i ) 286 { 287 final var pos = Character.digit( buffer.charAt( i ), RADIX ); 288 retValue [i] = (byte) ALPHABET_UPPERCASE [pos]; 289 } 290 291 //---* Done *------------------------------------------------------ 292 return retValue; 293 } // encode() 294 295 /** 296 * <p>{@summary Encodes the given value to Crockford's 297 * Base 32.}</p> 298 * <p>This is the converse operation to 299 * {@link Decoder#decodeToString(byte[])}.</p> 300 * 301 * @param value The value to encode. 302 * @return The result. 303 */ 304 public final byte [] encode( final String value ) 305 { 306 final byte [] retValue; 307 if( requireNonNullArgument( value, "value" ).isEmpty() ) 308 { 309 retValue = EMPTY_byte_ARRAY; 310 } 311 else 312 { 313 retValue = encode( value.getBytes( UTF8 ) ); 314 } 315 316 //---* Done *------------------------------------------------------ 317 return retValue; 318 } // encode() 319 320 /** 321 * <p>{@summary Encodes the given value to Crockford's 322 * Base 32.}</p> 323 * <p>This is the converse operation to 324 * {@link Decoder#decode(byte[])}.</p> 325 * 326 * @param value The value to encode. 327 * @return The result. 328 */ 329 public final byte [] encode( final byte [] value ) 330 { 331 final byte [] retValue; 332 if( requireNonNullArgument( value, "value" ).length == 0 ) 333 { 334 retValue = EMPTY_byte_ARRAY; 335 } 336 else 337 { 338 retValue = encode( new BigInteger( value ) ); 339 } 340 341 //---* Done *------------------------------------------------------ 342 return retValue; 343 } // encode() 344 345 /** 346 * Encodes the given value to Crockford's Base 32. 347 * 348 * @param value The value to encode. 349 * @return The result. 350 */ 351 public final byte [] encode( final long value ) 352 { 353 final var retValue = encode( BigInteger.valueOf( value ) ); 354 355 //---* Done *------------------------------------------------------ 356 return retValue; 357 } // encode() 358 359 /** 360 * <p>{@summary Encodes the given value to a Crockford's Base 32 361 * String.}</p> 362 * <p>This is the converse operation to 363 * {@link Decoder#decodeToNumber(String)}.</p> 364 * 365 * @param value The value to encode. 366 * @return The result. 367 */ 368 public final String encodeToString( final BigInteger value ) 369 { 370 final var retValue = new String( encode( value ), UTF8 ); 371 372 //---* Done *------------------------------------------------------ 373 return retValue; 374 } // encodeToString() 375 376 /** 377 * <p>{@summary Encodes the given value to a Crockford's Base 32 378 * String.}</p> 379 * <p>This is the converse operation to 380 * {@link Decoder#decodeToString(String)}.</p> 381 * 382 * @param value The value to encode. 383 * @return The result. 384 */ 385 public final String encodeToString( final String value ) 386 { 387 final var retValue = new String( encode( value ), UTF8 ); 388 389 //---* Done *------------------------------------------------------ 390 return retValue; 391 } // encodeToString() 392 393 /** 394 * <p>{@summary Encodes the given value to a Crockford's Base 32 395 * String.}</p> 396 * <p>This is the converse operation to 397 * {@link Decoder#decodeToString(byte[])}.</p> 398 * 399 * @param value The value to encode. 400 * @return The result. 401 */ 402 public final String encodeToString( final byte [] value ) 403 { 404 final var retValue = new String( encode( value ), UTF8 ); 405 406 //---* Done *------------------------------------------------------ 407 return retValue; 408 } // encodeToString() 409 410 /** 411 * Encodes the given value to a Crockford's Base 32 String. 412 * 413 * @param value The value to encode. 414 * @return The result. 415 */ 416 public final String encodeToString( final long value ) 417 { 418 final var retValue = new String( encode( value ), UTF8 ); 419 420 //---* Done *------------------------------------------------------ 421 return retValue; 422 } // encodeToString() 423 } // class Encoder 424 425 /*-----------*\ 426 ====** Constants **======================================================== 427 \*-----------*/ 428 /** 429 * The lowercase symbols for the Base 32 alphabet. 430 */ 431 @SuppressWarnings( "unused" ) 432 private static final char[] ALPHABET_LOWERCASE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z' }; 433 434 /** 435 * The uppercase symbols for the Base 32 alphabet. 436 */ 437 private static final char[] ALPHABET_UPPERCASE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' }; 438 439 /** 440 * An empty byte array. 441 */ 442 public static final byte[] EMPTY_byte_ARRAY = new byte [0]; 443 444 /** 445 * The radix for Base 32 is, as one would expect: {@value}. 446 */ 447 private static final int RADIX = 32; 448 449 /*------------------------*\ 450 ====** Static Initialisations **=========================================== 451 \*------------------------*/ 452 /** 453 * The symbol values. 454 */ 455 private static final long[] ALPHABET_VALUES; 456 457 /** 458 * The Base 32 decoder instance. 459 */ 460 private static final Decoder m_Decoder; 461 462 /** 463 * The Base 32 encoder instance. 464 */ 465 private static final Encoder m_Encoder; 466 467 static 468 { 469 //---* Initialise the symbol values *---------------------------------- 470 ALPHABET_VALUES = new long [128]; 471 fill( ALPHABET_VALUES, -1 ); // -1 indicates an invalid index 472 473 //---* The Numbers *--------------------------------------------------- 474 ALPHABET_VALUES['0'] = 0x00; 475 ALPHABET_VALUES['1'] = 0x01; 476 ALPHABET_VALUES['2'] = 0x02; 477 ALPHABET_VALUES['3'] = 0x03; 478 ALPHABET_VALUES['4'] = 0x04; 479 ALPHABET_VALUES['5'] = 0x05; 480 ALPHABET_VALUES['6'] = 0x06; 481 ALPHABET_VALUES['7'] = 0x07; 482 ALPHABET_VALUES['8'] = 0x08; 483 ALPHABET_VALUES['9'] = 0x09; 484 485 //---* The lower case letters *---------------------------------------- 486 ALPHABET_VALUES['a'] = 0x0a; 487 ALPHABET_VALUES['b'] = 0x0b; 488 ALPHABET_VALUES['c'] = 0x0c; 489 ALPHABET_VALUES['d'] = 0x0d; 490 ALPHABET_VALUES['e'] = 0x0e; 491 ALPHABET_VALUES['f'] = 0x0f; 492 ALPHABET_VALUES['g'] = 0x10; 493 ALPHABET_VALUES['h'] = 0x11; 494 ALPHABET_VALUES['j'] = 0x12; 495 ALPHABET_VALUES['k'] = 0x13; 496 ALPHABET_VALUES['m'] = 0x14; 497 ALPHABET_VALUES['n'] = 0x15; 498 ALPHABET_VALUES['p'] = 0x16; 499 ALPHABET_VALUES['q'] = 0x17; 500 ALPHABET_VALUES['r'] = 0x18; 501 ALPHABET_VALUES['s'] = 0x19; 502 ALPHABET_VALUES['t'] = 0x1a; 503 ALPHABET_VALUES['v'] = 0x1b; 504 ALPHABET_VALUES['w'] = 0x1c; 505 ALPHABET_VALUES['x'] = 0x1d; 506 ALPHABET_VALUES['y'] = 0x1e; 507 ALPHABET_VALUES['z'] = 0x1f; 508 509 //---* The lower case OIL *-------------------------------------------- 510 ALPHABET_VALUES['o'] = 0x00; 511 ALPHABET_VALUES['i'] = 0x01; 512 ALPHABET_VALUES['l'] = 0x01; 513 514 //---* The upper case letters *---------------------------------------- 515 ALPHABET_VALUES['A'] = 0x0a; 516 ALPHABET_VALUES['B'] = 0x0b; 517 ALPHABET_VALUES['C'] = 0x0c; 518 ALPHABET_VALUES['D'] = 0x0d; 519 ALPHABET_VALUES['E'] = 0x0e; 520 ALPHABET_VALUES['F'] = 0x0f; 521 ALPHABET_VALUES['G'] = 0x10; 522 ALPHABET_VALUES['H'] = 0x11; 523 ALPHABET_VALUES['J'] = 0x12; 524 ALPHABET_VALUES['K'] = 0x13; 525 ALPHABET_VALUES['M'] = 0x14; 526 ALPHABET_VALUES['N'] = 0x15; 527 ALPHABET_VALUES['P'] = 0x16; 528 ALPHABET_VALUES['Q'] = 0x17; 529 ALPHABET_VALUES['R'] = 0x18; 530 ALPHABET_VALUES['S'] = 0x19; 531 ALPHABET_VALUES['T'] = 0x1a; 532 ALPHABET_VALUES['V'] = 0x1b; 533 ALPHABET_VALUES['W'] = 0x1c; 534 ALPHABET_VALUES['X'] = 0x1d; 535 ALPHABET_VALUES['Y'] = 0x1e; 536 ALPHABET_VALUES['Z'] = 0x1f; 537 538 //---* The upper case OIL *-------------------------------------------- 539 ALPHABET_VALUES['O'] = 0x00; 540 ALPHABET_VALUES['I'] = 0x01; 541 ALPHABET_VALUES['L'] = 0x01; 542 543 //---* The instances *------------------------------------------------- 544 m_Decoder = new Decoder(); 545 m_Encoder = new Encoder(); 546 } 547 548 /*--------------*\ 549 ====** Constructors **===================================================== 550 \*--------------*/ 551 /** 552 * No instance allowed for this class. 553 */ 554 private Base32() { throw new PrivateConstructorForStaticClassCalledError( Base32.class ); } 555 556 /*---------*\ 557 ====** Methods **========================================================== 558 \*---------*/ 559 /** 560 * Returns a decoder for 561 * {@href https://www.crockford.com/base32.html Crockfords's Base 32} 562 * format. 563 * 564 * @return The decoder. 565 */ 566 public static final Decoder getDecoder() { return m_Decoder; } 567 568 /** 569 * Returns an encoder for 570 * {@href https://www.crockford.com/base32.html Crockfords's Base 32} 571 * format. 572 * 573 * @return The encoder. 574 */ 575 public static final Encoder getEncoder() { return m_Encoder; } 576} 577// class Base32 578 579/* 580 * End of File 581 */