001/* 002 * ============================================================================ 003 * Copyright © 2002-2026 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.internal; 019 020import static java.nio.file.StandardOpenOption.READ; 021import static org.apiguardian.api.API.Status.INTERNAL; 022import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 023import static org.tquadrat.foundation.lang.Objects.requireNotBlankArgument; 024import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 025import static org.tquadrat.foundation.util.HexUtils.convertFromHexString; 026import static org.tquadrat.foundation.util.IOUtils.DEFAULT_BUFFER_SIZE; 027 028import java.io.IOException; 029import java.io.Serial; 030import java.nio.file.Files; 031import java.nio.file.Path; 032import java.security.MessageDigest; 033import java.util.Arrays; 034import java.util.HexFormat; 035import java.util.zip.Checksum; 036 037import org.apiguardian.api.API; 038import org.tquadrat.foundation.annotation.ClassVersion; 039import org.tquadrat.foundation.annotation.NotRecord; 040import org.tquadrat.foundation.exception.UnlikelyExceptionError; 041import org.tquadrat.foundation.util.Hash; 042 043/** 044 * The implementation for the interface 045 * {@link Hash}. 046 * 047 * @version $Id: HashImpl.java 1151 2025-10-01 21:32:15Z tquadrat $ 048 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 049 * @UMLGraph.link 050 * @since 0.1.1 051 */ 052@SuppressWarnings( "SerializableDeserializableClassInSecureContext" ) 053@ClassVersion( sourceVersion = "$Id: HashImpl.java 1151 2025-10-01 21:32:15Z tquadrat $" ) 054@API( status = INTERNAL, since = "0.1.1" ) 055@NotRecord 056public final class HashImpl implements Hash 057{ 058 /*------------*\ 059 ====** Attributes **======================================================= 060 \*------------*/ 061 /** 062 * The hash value. 063 * 064 * @serial 065 */ 066 private final byte [] m_HashValue; 067 068 /*------------------------*\ 069 ====** Static Initialisations **=========================================== 070 \*------------------------*/ 071 /** 072 * The serial version UID for objects of this class: {@value}. 073 * 074 * @hidden 075 */ 076 @Serial 077 private static final long serialVersionUID = 539879857L; 078 079 /*--------------*\ 080 ====** Constructors **===================================================== 081 \*--------------*/ 082 /** 083 * <p>{@summary {@constructor}}</p> 084 * 085 * @param hashValue The hash value. 086 */ 087 public HashImpl( final byte [] hashValue ) 088 { 089 m_HashValue = requireNotEmptyArgument( hashValue, "hashValue" ); 090 } // HashImpl() 091 092 /*---------*\ 093 ====** Methods **========================================================== 094 \*---------*/ 095 /** 096 * {@inheritDoc} 097 */ 098 @Override 099 public final byte [] bytes() { return m_HashValue.clone(); } 100 101 /** 102 * {@inheritDoc} 103 */ 104 @Override 105 public final HashImpl clone() 106 { 107 final HashImpl retValue; 108 try 109 { 110 retValue = (HashImpl) super.clone(); 111 } 112 catch( final CloneNotSupportedException e ) 113 { 114 throw new UnlikelyExceptionError( e ); 115 } 116 117 //---* Done *---------------------------------------------------------- 118 return retValue; 119 } // clone() 120 121 /** 122 * Creates the hash for the given byte array, using the given algorithm. 123 * 124 * @param data The input data. 125 * @param algorithm The algorithm 126 * @return A new instance of {@code HashImpl}. 127 */ 128 public static final HashImpl create( final byte [] data, final Checksum algorithm ) 129 { 130 requireNonNullArgument( algorithm, "algorithm" ).reset(); 131 algorithm.update( requireNonNullArgument( data, "data" ) ); 132 final var hashValue = algorithm.getValue(); 133 final var retValue = from( Long.toHexString( hashValue ) ); 134 135 //---* Done *---------------------------------------------------------- 136 return retValue; 137 } // create() 138 139 /** 140 * Creates the hash for the given file, using the given algorithm. 141 * 142 * @param data The input data. 143 * @param algorithm The algorithm 144 * @return A new instance of {@code HashImpl}. 145 * @throws IOException Problems to process the file. 146 */ 147 public static Hash create( final Path data, final Checksum algorithm ) throws IOException 148 { 149 requireNonNullArgument( algorithm, "algorithm" ).reset(); 150 try( final var inputStream = Files.newInputStream( requireNonNullArgument( data, "data" ), READ ) ) 151 { 152 final var buffer = new byte [DEFAULT_BUFFER_SIZE]; 153 var readBytes = 0; 154 //noinspection NestedAssignment 155 while( (readBytes = inputStream.read( buffer )) > 0 ) 156 { 157 algorithm.update( buffer, 0, readBytes ); 158 } 159 } 160 final var retValue = from( Long.toHexString( algorithm.getValue() ) ); 161 162 //---* Done *---------------------------------------------------------- 163 return retValue; 164 } // create() 165 166 /** 167 * Creates the hash for the given byte array, using the given algorithm. 168 * 169 * @param data The input data. 170 * @param algorithm The algorithm 171 * @return A new instance of {@code HashImpl}. 172 */ 173 public static final HashImpl create( final byte [] data, final MessageDigest algorithm ) 174 { 175 requireNonNullArgument( algorithm, "algorithm" ).reset(); 176 final var hashValue = algorithm.digest( requireNonNullArgument( data, "data" ) ); 177 final var retValue = new HashImpl( hashValue ); 178 179 //---* Done *---------------------------------------------------------- 180 return retValue; 181 } // create() 182 183 /** 184 * Creates the hash for the given file, using the given algorithm. 185 * 186 * @param data The input data. 187 * @param algorithm The algorithm 188 * @throws IOException Problems to process the file. 189 * @return A new instance of {@code HashImpl}. 190 */ 191 public static Hash create( final Path data, final MessageDigest algorithm ) throws IOException 192 { 193 requireNonNullArgument( algorithm, "algorithm" ).reset(); 194 try( final var inputStream = Files.newInputStream( requireNonNullArgument( data, "data" ), READ ) ) 195 { 196 final var buffer = new byte [DEFAULT_BUFFER_SIZE]; 197 var readBytes = 0; 198 //noinspection NestedAssignment 199 while( (readBytes = inputStream.read( buffer )) > 0 ) 200 { 201 algorithm.update( buffer, 0, readBytes ); 202 } 203 } 204 final var retValue = new HashImpl( algorithm.digest() ); 205 206 //---* Done *---------------------------------------------------------- 207 return retValue; 208 } // create() 209 210 /** 211 * {@inheritDoc} 212 */ 213 @Override 214 public final boolean equals( final Object o ) 215 { 216 var retValue = this == o; 217 if( !retValue && o instanceof final HashImpl other ) 218 { 219 retValue = Arrays.equals( m_HashValue, other.m_HashValue ); 220 } 221 222 //---* Done *---------------------------------------------------------- 223 return retValue; 224 } // equals() 225 226 /** 227 * Creates an instance of {@code Hash} from the given String. 228 * 229 * @param hashValue The hash value. 230 * @return A new instance of {@code HashImpl}. 231 */ 232 public static final HashImpl from( final CharSequence hashValue ) 233 { 234 final var retValue = new HashImpl( convertFromHexString( requireNotBlankArgument( hashValue, "hashValue" ) ) ); 235 236 //---* Done *---------------------------------------------------------- 237 return retValue; 238 } // from() 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override 244 public final int hashCode() { return Arrays.hashCode( m_HashValue ); } 245 246 /** 247 * {@inheritDoc} 248 */ 249 @Override 250 public final String toString() { return HexFormat.of().formatHex( m_HashValue ); } 251} 252// class HashImpl 253 254/* 255 * End of File 256 */