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