3434import com .google .common .io .BaseEncoding ;
3535import java .io .Serializable ;
3636import java .math .BigInteger ;
37+ import java .nio .ByteBuffer ;
3738import java .util .AbstractMap ;
38- import java .util .Arrays ;
3939import java .util .Collections ;
4040import java .util .HashMap ;
4141import java .util .List ;
@@ -232,6 +232,7 @@ public abstract static class Builder {
232232 *
233233 * @see <a href="https://cloud.google.com/storage/docs/hashes-etags#_JSONAPI">Hashes and ETags:
234234 * Best Practices</a>
235+ * @throws IllegalArgumentException when given an invalid hexadecimal value.
235236 */
236237 public abstract Builder setMd5FromHexString (String md5HexString );
237238
@@ -252,6 +253,7 @@ public abstract static class Builder {
252253 *
253254 * @see <a href="https://cloud.google.com/storage/docs/hashes-etags#_JSONAPI">Hashes and ETags:
254255 * Best Practices</a>
256+ * @throws IllegalArgumentException when given an invalid hexadecimal value.
255257 */
256258 public abstract Builder setCrc32cFromHexString (String crc32cHexString );
257259
@@ -293,7 +295,7 @@ public abstract static class Builder {
293295 }
294296
295297 static final class BuilderImpl extends Builder {
296-
298+ private final String hexDecimalValues = "0123456789abcdef" ;
297299 private BlobId blobId ;
298300 private String generatedId ;
299301 private String contentType ;
@@ -442,16 +444,27 @@ public Builder setMd5(String md5) {
442444 return this ;
443445 }
444446
447+ @ Override
445448 public Builder setMd5FromHexString (String md5HexString ) {
446449 if (md5HexString == null ) {
447450 return this ;
448451 }
449- byte [] bytes = new BigInteger (md5HexString , 16 ).toByteArray ();
450- int leadingEmptyBytes = bytes .length - md5HexString .length () / 2 ;
451- if (leadingEmptyBytes > 0 ) {
452- bytes = Arrays .copyOfRange (bytes , leadingEmptyBytes , bytes .length );
452+ if (md5HexString .length () % 2 != 0 ) {
453+ throw new IllegalArgumentException (
454+ "each byte must be represented by 2 valid hexadecimal characters" );
455+ }
456+ String md5HexStringLower = md5HexString .toLowerCase ();
457+ ByteBuffer md5ByteBuffer = ByteBuffer .allocate (md5HexStringLower .length () / 2 );
458+ for (int charIndex = 0 ; charIndex < md5HexStringLower .length (); charIndex += 2 ) {
459+ int higherOrderBits = this .hexDecimalValues .indexOf (md5HexStringLower .charAt (charIndex ));
460+ int lowerOrderBits = this .hexDecimalValues .indexOf (md5HexStringLower .charAt (charIndex + 1 ));
461+ if (higherOrderBits == -1 || lowerOrderBits == -1 ) {
462+ throw new IllegalArgumentException (
463+ "each byte must be represented by 2 valid hexadecimal characters" );
464+ }
465+ md5ByteBuffer .put ((byte ) (higherOrderBits << 4 | lowerOrderBits ));
453466 }
454- this .md5 = BaseEncoding .base64 ().encode (bytes );
467+ this .md5 = BaseEncoding .base64 ().encode (md5ByteBuffer . array () );
455468 return this ;
456469 }
457470
@@ -466,12 +479,23 @@ public Builder setCrc32cFromHexString(String crc32cHexString) {
466479 if (crc32cHexString == null ) {
467480 return this ;
468481 }
469- byte [] bytes = new BigInteger (crc32cHexString , 16 ).toByteArray ();
470- int leadingEmptyBytes = bytes .length - crc32cHexString .length () / 2 ;
471- if (leadingEmptyBytes > 0 ) {
472- bytes = Arrays .copyOfRange (bytes , leadingEmptyBytes , bytes .length );
482+ if (crc32cHexString .length () % 2 != 0 ) {
483+ throw new IllegalArgumentException (
484+ "each byte must be represented by 2 valid hexadecimal characters" );
485+ }
486+ String crc32cHexStringLower = crc32cHexString .toLowerCase ();
487+ ByteBuffer crc32cByteBuffer = ByteBuffer .allocate (crc32cHexStringLower .length () / 2 );
488+ for (int charIndex = 0 ; charIndex < crc32cHexStringLower .length (); charIndex += 2 ) {
489+ int higherOrderBits = this .hexDecimalValues .indexOf (crc32cHexStringLower .charAt (charIndex ));
490+ int lowerOrderBits =
491+ this .hexDecimalValues .indexOf (crc32cHexStringLower .charAt (charIndex + 1 ));
492+ if (higherOrderBits == -1 || lowerOrderBits == -1 ) {
493+ throw new IllegalArgumentException (
494+ "each byte must be represented by 2 valid hexadecimal characters" );
495+ }
496+ crc32cByteBuffer .put ((byte ) (higherOrderBits << 4 | lowerOrderBits ));
473497 }
474- this .crc32c = BaseEncoding .base64 ().encode (bytes );
498+ this .crc32c = BaseEncoding .base64 ().encode (crc32cByteBuffer . array () );
475499 return this ;
476500 }
477501
0 commit comments