ref: 6b8fc490777700f00cd198585a8759b3d1f5f351
parent: 61f1bac9ff383ba5958c838b7921f846d2f4277c
parent: 78502ebcdfeb369f388df98493f07c55a5987774
author: Ethan Hugg <[email protected]>
date: Wed Jan 8 15:25:41 EST 2014
Merge pull request #121 from ekr/add_gtests Add gtests for encoder and decoder.
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,5 +9,5 @@
- sudo apt-get update -qq
- sudo apt-get install -qq nasm g++-4.6-multilib gcc-multilib libc6-dev-i386 lib32z1-dev
install: make gtest-bootstrap
-script: make && make test && make clean && make ENABLE64BIT=Yes && make test && make clean && make BUILDTYPE=Release && make test && make BUILDTYPE=Release clean && make ENABLE64BIT=Yes BUILDTYPE=Release && make test
+script: make ENABLE64BIT=Yes && make test && make clean && make ENABLE64BIT=Yes BUILDTYPE=Release && make test
--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,7 @@
H264ENC_LDFLAGS = -L. -lencoder -lprocessing -lcommon
H264ENC_DEPS = $(LIBPREFIX)encoder.$(LIBSUFFIX) $(LIBPREFIX)processing.$(LIBSUFFIX) $(LIBPREFIX)common.$(LIBSUFFIX)
-CODEC_UNITTEST_LDFLAGS = -L. -lgtest -ldecoder -lcommon
+CODEC_UNITTEST_LDFLAGS = -L. -lgtest -ldecoder -lcrypto -lencoder -lprocessing -lcommon
CODEC_UNITTEST_DEPS = $(LIBPREFIX)gtest.$(LIBSUFFIX) $(LIBPREFIX)decoder.$(LIBSUFFIX) $(LIBPREFIX)common.$(LIBSUFFIX)
.PHONY: test
--- /dev/null
+++ b/res/CiscoVT2people_160x96_6fps.yuv
@@ -1,0 +1,1 @@
+��������������������v]TQOOSVWTOLOIMRXUPc�����������������������������������������������������������������������������������������������������������������������鮮������������������a`QNPVYVSPLJLLFLOROL]����������������������������������������������������������������������������������������������������������������������鯯�����������������ahYRNTZTQSJJGGGIEHKHKM[���������������������������������������������������������������������������������������������������������������������鯯�����������������Vd[UX]WNQPMLIGGFIHKHKNO���������������������������������������������������������������������������������������������������������������������魭����������������jSb_^debdbc`b\PRJPQQQNQQk��������������������������������������������������������������������������������������������������������������������뭭����������������]S\Yc�������~ld\^jl_WONTQ�������������������������������������������������������������������������{edr���������������������������������������뮮����������������WPYU����ķ���������taSCPL|�����������������������������������������������������������������}z}�yj^VSNTWfo�����������������������������������뺴����������������STW]����������������p\KHJd��������������������������������������������������������������pb`^UQWRRRVYYSNWblp�����������������������������������ɺ����������������QT]m����Ȼ�����������o\BGZ������������������������������������������������������������ybLQ]L?BGMPUWWYVQQ]nf]��������������������������������빶����������������PZdz����Ȼ�����������wuEGZ�����������������������������������������������������������pTINYWD?HJJMRWWVSUalqjUP{�������������������������������벱����������������Tcp������ı�����������{TGY����������������������������������������������������������hQKKRUM@HWKBGPTSXUTcgnhRFRy������������������������������뭮����������������ajy������ǹ�����������xWHQ���������������������������������������������������������hRMBN_\F?NWO@DLSTUX^abSMRKEO������������������������������뫫����������������wq{���緜xz�����������|YEO��������������������������������������������������������rTOBDOXWICEQPJB>HV[^cgePBFHHLc�����������������������������몪�����������������r���仙��vp����h\bq��YGT�������������������������������������������������������|WZFEEFOZSGFSNHMPDMU^ki^PB?AAFO�����������������������������몪�����������������v�����������vgdg_i|�fKq�������������������������������������������������������d^OEFD>L[XKKQSSIVRKZ_ZTSJD>;>AGX����������������������������몪����������������ļ������ۭ�����rq����}�xH�������������������������������������������������������r_[RNB:AOX[PTSZZVPcbKiYGFC>>;>AGIv���������������������������먨�����������������Ϯ����͉EVn�٤�s�������^������������������������������������������������������xd`ZUNA@DHM[^knpsi^goUOUE==;>==>AHR���������������������������맧�����������������������翓���佖xePUi���ng�����������������������������������������������������gg__^LBMJHTh~����}wodXTKB<=><=<;DGOm��������������������������먨������������������������ģ����ơ�}������g�����������������������������������������������������teY]cWDOROTe��������m_XLL@;;==;>>ADJW��������������������������먨����������������������������ʤ��������q�����������������������������������������������������daZ\_TNYYVby�������t_^LLF??==;>A>DJT��������������������������륦�����������������������Կ�����̡�������y������������������������������������������������������k_YY[MTVQbg����ɿ��zsaZTLM>>==;;C@CEMt�������������������������륦������������������������ȼ����Ъ�������������������������������������������������������������wfZ\VQJTONav�����ǻ��ki]XPQG;<<>><?CELa�������������������������뢤�����������������������κ�������������������������������������������������������������������oa^WSGJNKRi�������¶�s`]ZTLJ;<<==;>@FMR�������������������������롤�����������������|������ѷ��i��cY������������������������������������������������������������i][TODGDHYw������ο���hZ]XUI:99==;><<GK�������������������������뤢��������������}}���������ƺ��|x�����������������������������������������������������������{b[ZQJ@CBLg��������ĵ���c][VQB6:9?;;::BK|������������������������뤢��������������||zys��������ɽ���������������������������������������������������������������x
\ No newline at end of file
--- /dev/null
+++ b/res/CiscoVT2people_320x192_12fps.yuv
@@ -1,0 +1,1 @@
+�����������������������������������������s^XSTTPQMMOPRTVZWTQROMNQNJFJQURY[XSOVj������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������豱��������������������������������������j``[SRPPQMNRSTUVSTTUNJHIOMJIIMPPUWVRNKMTx����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������议�������������������������������������y[^h`TNMPONRVXWVTQRRRPLJMMKKJFGKOORSRKLJLY{���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������譭�������������������������������������`gbaVTLJPOPTZ[YVUUSOMLHFHJKKLECFMJOQPPQNJJT|�������������������������������������Ļ�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������谰������������������������������������yVbc\QQPMKMV[YXVTRXSMIJHGFHEEHIHECIJKMIIHIIMS������������������������������������ļ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������议������������������������������������]VmkaURQQOQY[WQPONQOJFKIHFHFFHHHFEEGHIEGKNNNEP�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������误�����������������������������������yR[ff^SSUVVY][SLMNONNKIGGECGFFGFFGHEFHIDHKNLPMKx����������������������������������Ŀ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������议�����������������������������������\QV^c`XUWY[^`ZRNORSQPPOPOMMGEEGDEHJHJLMHIIHMMQP`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������譭����������������������������������YNWdfc[U^Zdc`YX]ZWY\YXZXWUSLFOKHGHJKJLPMMLKPRQOM������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������뭭����������������������������������vWPU\bb\^fhjp]jlkmlkmifgljdaWTWTIOUXUVTRTSPONRRPSt�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������뭭����������������������������������hUSRX^^X`bu�|kz~�����{z{ukf^^WXOYabef`WZVROLQTRUS�����������������������������������������������������������������������������������������������������������������������������������������������������wy����������������������������������������������������������������������������������뭭����������������������������������_TTPZ]YRUt����������������sjik_aamsopqi[XTNJJOUUQJu������������������������������������������������������������������������������������������������������������������������������������������������vjVKOJJTbru~����������������������������������������������������������������������������묭����������������������������������[RMQV[\Qc������ƿ������������}�|��|z}�rpcXQPK?RPOK`�������������������������������������������������������������������������������������������������������������������������������������������~unc\USQODGNV[^hqx��������
\ No newline at end of file
binary files /dev/null b/res/test_vd_1d.264 differ
binary files /dev/null b/res/test_vd_rc.264 differ
--- /dev/null
+++ b/test/decoder_test.cpp
@@ -1,0 +1,194 @@
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <limits.h>
+#include <fstream>
+#include <openssl/sha.h>
+
+#include "codec_def.h"
+#include "codec_app_def.h"
+#include "codec_api.h"
+
+#include "utils/BufferedData.h"
+#include "utils/HashFunctions.h"
+
+static void UpdateHashFromPlane(SHA_CTX* ctx, const uint8_t* plane,
+ int width, int height, int stride) {
+ for (int i = 0; i < height; i++) {
+ SHA1_Update(ctx, plane, width);
+ plane += stride;
+ }
+}
+
+/**
+ * @return frame size (>= 0), or -1 for memory allocation error.
+ */
+static int ReadFrame(std::ifstream* file, BufferedData* buf) {
+ // start code of a frame is {0, 0, 0, 1}
+ int zeroCount = 0;
+ char b;
+
+ for (;;) {
+ file->read(&b, 1);
+ if (file->gcount() != 1) {
+ break;
+ }
+ if (!buf->Push(b)) {
+ return -1;
+ }
+
+ if (buf->Length() <= 4) {
+ continue;
+ }
+
+ if (zeroCount < 3) {
+ zeroCount = b != 0 ? 0 : zeroCount + 1;
+ } else {
+ if (b == 1) {
+ if (file->seekg(-4, file->cur).good()) {
+ return buf->Length() - 4;
+ } else {
+ // seeking fails
+ return -1;
+ }
+ } else if (b == 0) {
+ zeroCount = 3;
+ } else {
+ zeroCount = 0;
+ }
+ }
+ }
+ return buf->Length();
+}
+
+/**
+ * @return true if a frame is decoded successfully, otherwise false.
+ */
+static bool DecodeAndProcess(ISVCDecoder* decoder, const uint8_t* src,
+ int sliceSize, SHA_CTX* ctx) {
+ void* data[3];
+ SBufferInfo bufInfo;
+ memset(data, 0, sizeof(data));
+ memset(&bufInfo, 0, sizeof(SBufferInfo));
+
+ DECODING_STATE rv = decoder->DecodeFrame(src, sliceSize, data, &bufInfo);
+ if (rv != dsErrorFree) {
+ return false;
+ }
+
+ if (bufInfo.iBufferStatus == 1) {
+ // y plane
+ UpdateHashFromPlane(ctx, static_cast<uint8_t*>(data[0]),
+ bufInfo.UsrData.sSystemBuffer.iWidth,
+ bufInfo.UsrData.sSystemBuffer.iHeight,
+ bufInfo.UsrData.sSystemBuffer.iStride[0]);
+ // u plane
+ UpdateHashFromPlane(ctx, static_cast<uint8_t*>(data[1]),
+ bufInfo.UsrData.sSystemBuffer.iWidth / 2,
+ bufInfo.UsrData.sSystemBuffer.iHeight / 2,
+ bufInfo.UsrData.sSystemBuffer.iStride[1]);
+ // v plane
+ UpdateHashFromPlane(ctx, static_cast<uint8_t*>(data[2]),
+ bufInfo.UsrData.sSystemBuffer.iWidth / 2,
+ bufInfo.UsrData.sSystemBuffer.iHeight / 2,
+ bufInfo.UsrData.sSystemBuffer.iStride[1]);
+ }
+ return true;
+}
+
+static void CompareFileToHash(ISVCDecoder* decoder,
+ const char* fileName, const char* hashStr) {
+ std::ifstream file(fileName, std::ios::in | std::ios::binary);
+ ASSERT_TRUE(file.is_open());
+
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+
+ BufferedData buf;
+ int sliceSize;
+
+ while ((sliceSize = ReadFrame(&file, &buf)) > 0) {
+ if (DecodeAndProcess(decoder, buf.data(), sliceSize, &ctx)) {
+ buf.Clear();
+ } else {
+ SHA1_Final(digest, &ctx);
+ FAIL() << "unable to decode frame";
+ }
+ }
+
+ if (sliceSize < 0) {
+ SHA1_Final(digest, &ctx);
+ FAIL() << "unable to allocate memory";
+ }
+
+ int32_t iEndOfStreamFlag = 1;
+ decoder->SetOption(DECODER_OPTION_END_OF_STREAM, &iEndOfStreamFlag);
+
+ // Get pending last frame
+ if (!DecodeAndProcess(decoder, NULL, 0, &ctx)) {
+ SHA1_Final(digest, &ctx);
+ FAIL() << "unable to decode last frame";
+ }
+
+ SHA1_Final(digest, &ctx);
+ ASSERT_TRUE(CompareHash(digest, hashStr));
+}
+
+class DecoderInitTest : public ::testing::Test {
+ public:
+ DecoderInitTest() : decoder_(NULL) {}
+
+ virtual void SetUp() {
+ long rv = CreateDecoder(&decoder_);
+ ASSERT_EQ(0, rv);
+ ASSERT_TRUE(decoder_ != NULL);
+
+ SDecodingParam decParam;
+ memset(&decParam, 0, sizeof(SDecodingParam));
+ decParam.iOutputColorFormat = videoFormatI420;
+ decParam.uiTargetDqLayer = UCHAR_MAX;
+ decParam.uiEcActiveFlag = 1;
+ decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
+
+ rv = decoder_->Initialize(&decParam, INIT_TYPE_PARAMETER_BASED);
+ ASSERT_EQ(0, rv);
+ }
+
+ virtual void TearDown() {
+ if (decoder_ != NULL) {
+ decoder_->Uninitialize();
+ DestroyDecoder(decoder_);
+ }
+ }
+
+ protected:
+ ISVCDecoder* decoder_;
+};
+
+
+TEST_F(DecoderInitTest, JustInit) {
+}
+
+struct FileParam {
+ const char* fileName;
+ const char* hashStr;
+};
+
+class DecoderOutputTest : public DecoderInitTest,
+ public ::testing::WithParamInterface<FileParam> {
+};
+
+TEST_P(DecoderOutputTest, CompareOutput) {
+ FileParam p = GetParam();
+ CompareFileToHash(decoder_, p.fileName, p.hashStr);
+}
+
+static const FileParam kFileParamArray[] = {
+ {"res/test_vd_1d.264", "5827d2338b79ff82cd091c707823e466197281d3"},
+ {"res/test_vd_rc.264", "eea02e97bfec89d0418593a8abaaf55d02eaa1ca"}
+};
+
+INSTANTIATE_TEST_CASE_P(DecodeFile, DecoderOutputTest,
+ ::testing::ValuesIn(kFileParamArray));
--- /dev/null
+++ b/test/encoder_test.cpp
@@ -1,0 +1,150 @@
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <limits.h>
+#include <fstream>
+
+#include "codec_def.h"
+#include "codec_app_def.h"
+#include "codec_api.h"
+
+#include "utils/BufferedData.h"
+#include "utils/HashFunctions.h"
+
+static int InitWithParam(ISVCEncoder* encoder, int width,
+ int height, float frameRate) {
+ SVCEncodingParam param;
+ memset (¶m, 0, sizeof(SVCEncodingParam));
+
+ param.sSpatialLayers[0].iVideoWidth = width;
+ param.sSpatialLayers[0].iVideoHeight = height;
+ param.sSpatialLayers[0].fFrameRate = frameRate;
+ param.sSpatialLayers[0].iQualityLayerNum = 1;
+ param.sSpatialLayers[0].iSpatialBitrate = 600000;
+
+ SSliceConfig* sliceCfg = ¶m.sSpatialLayers[0].sSliceCfg;
+ sliceCfg->sSliceArgument.uiSliceNum = 1;
+ sliceCfg->sSliceArgument.uiSliceSizeConstraint = 1500;
+ sliceCfg->sSliceArgument.uiSliceMbNum[0] = 960;
+
+ param.fFrameRate = param.sSpatialLayers[0].fFrameRate;
+ param.iPicWidth = param.sSpatialLayers[0].iVideoWidth;
+ param.iPicHeight = param.sSpatialLayers[0].iVideoHeight;
+ param.iTargetBitrate = 5000000;
+ param.iTemporalLayerNum = 3;
+ param.iSpatialLayerNum = 1;
+ param.bEnableBackgroundDetection = true;
+ param.bEnableLongTermReference = true;
+ param.iLtrMarkPeriod = 30;
+ param.iInputCsp = videoFormatI420;
+ param.bEnableSpsPpsIdAddition = true;
+
+ return encoder->Initialize(¶m, INIT_TYPE_PARAMETER_BASED);
+}
+
+static void UpdateHashFromFrame(const SFrameBSInfo& info, SHA_CTX* ctx) {
+ for (int i = 0; i < info.iLayerNum; ++i) {
+ const SLayerBSInfo& layerInfo = info.sLayerInfo[i];
+ int layerSize = 0;
+ for (int j = 0; j < layerInfo.iNalCount; ++j) {
+ layerSize += layerInfo.iNalLengthInByte[j];
+ }
+ SHA1_Update(ctx, layerInfo.pBsBuf, layerSize);
+ }
+}
+
+static void CompareFileToHash(ISVCEncoder* encoder,
+ const char* fileName, const char* hashStr,
+ int width, int height, float frameRate) {
+ std::ifstream file(fileName, std::ios::in | std::ios::binary);
+ ASSERT_TRUE(file.is_open());
+
+ int rv = InitWithParam(encoder, width, height, frameRate);
+ ASSERT_TRUE(rv == cmResultSuccess);
+
+ // I420: 1(Y) + 1/4(U) + 1/4(V)
+ int frameSize = width * height * 3 / 2;
+
+ BufferedData buf;
+ buf.SetLength(frameSize);
+ ASSERT_TRUE(buf.Length() == frameSize);
+ char* data = reinterpret_cast<char*>(buf.data());
+
+ SFrameBSInfo info;
+ memset(&info, 0, sizeof(SFrameBSInfo));
+
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+
+ while (file.read(data, frameSize), file.gcount() == frameSize) {
+ rv = encoder->EncodeFrame(buf.data(), &info);
+ if (rv == videoFrameTypeInvalid) {
+ SHA1_Final(digest, &ctx);
+ FAIL() << "unable to encode frame";
+ }
+ if (rv != videoFrameTypeSkip) {
+ UpdateHashFromFrame(info, &ctx);
+ }
+ }
+
+ SHA1_Final(digest, &ctx);
+ ASSERT_TRUE(CompareHash(digest, hashStr));
+}
+
+class EncoderBaseTest : public ::testing::Test {
+ public:
+ EncoderBaseTest() : encoder_(NULL) {}
+
+ virtual void SetUp() {
+ int rv = CreateSVCEncoder(&encoder_);
+ ASSERT_EQ(0, rv);
+ ASSERT_TRUE(encoder_ != NULL);
+ }
+
+ virtual void TearDown() {
+ if (encoder_) {
+ encoder_->Uninitialize();
+ DestroySVCEncoder(encoder_);
+ }
+ }
+
+ protected:
+ ISVCEncoder* encoder_;
+};
+
+TEST_F(EncoderBaseTest, JustInit) {
+}
+
+struct EncodeFileParam {
+ const char* fileName;
+ const char* hashStr;
+ int width;
+ int height;
+ float frameRate;
+};
+
+class EncoderOutputTest : public EncoderBaseTest ,
+ public ::testing::WithParamInterface<EncodeFileParam> {
+};
+
+
+TEST_P(EncoderOutputTest, CompareOutput) {
+ EncodeFileParam p = GetParam();
+ CompareFileToHash(encoder_, p.fileName, p.hashStr, p.width, p.height, p.frameRate);
+}
+
+static const EncodeFileParam kFileParamArray[] = {
+ {
+ "res/CiscoVT2people_320x192_12fps.yuv",
+ "4df5751a59eb02153e086ade9b3ecfcb8845c30b", 320, 192, 12.0f
+ },
+ {
+ "res/CiscoVT2people_160x96_6fps.yuv",
+ "6eb53b6bfdb95dfca0575bd3efe81aa58163951c", 160, 96, 6.0f
+ },
+};
+
+INSTANTIATE_TEST_CASE_P(EncodeFile, EncoderOutputTest,
+ ::testing::ValuesIn(kFileParamArray));
--- a/test/simple_test.cpp
+++ b/test/simple_test.cpp
@@ -1,38 +1,4 @@
#include <gtest/gtest.h>
-#if defined (WIN32)
-#include <windows.h>
-#include <tchar.h>
-#else
-#include <string.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include "codec_def.h"
-#include "codec_app_def.h"
-#include "codec_api.h"
-
-class CodecTest : public ::testing::Test {
- public:
- CodecTest() : decoder_ (NULL) {}
-
- ~CodecTest() {
- if (decoder_) DestroyDecoder (decoder_);
- }
-
- void SetUp() {
- long rv = CreateDecoder (&decoder_);
- ASSERT_EQ (0, rv);
- ASSERT_TRUE (decoder_);
- }
-
- protected:
- ISVCDecoder* decoder_;
-};
-
-TEST_F (CodecTest, JustInit) {
-}
int main (int argc, char** argv) {
testing::InitGoogleTest (&argc, argv);
--- a/test/targets.mk
+++ b/test/targets.mk
@@ -1,6 +1,8 @@
CODEC_UNITTEST_PREFIX=CODEC_UNITTEST
CODEC_UNITTEST_SRCDIR=test
CODEC_UNITTEST_CPP_SRCS=\
+ $(CODEC_UNITTEST_SRCDIR)/./decoder_test.cpp\
+ $(CODEC_UNITTEST_SRCDIR)/./encoder_test.cpp\
$(CODEC_UNITTEST_SRCDIR)/./simple_test.cpp\
CODEC_UNITTEST_OBJS += $(CODEC_UNITTEST_CPP_SRCS:.cpp=.o)
--- /dev/null
+++ b/test/utils/BufferedData.h
@@ -1,0 +1,63 @@
+#ifndef __BUFFEREDDATA_H__
+#define __BUFFEREDDATA_H__
+
+#include <stddef.h>
+#include <stdlib.h>
+
+class BufferedData {
+ public:
+ BufferedData() : data_(NULL), capacity_(0), length_(0) {}
+
+ ~BufferedData() {
+ free(data_);
+ }
+
+ bool Push(uint8_t c) {
+ if (!EnsureCapacity(length_ + 1)) {
+ return false;
+ }
+ data_[length_++] = c;
+ return true;
+ }
+
+ void Clear() {
+ length_ = 0;
+ }
+
+ void SetLength(size_t newLen) {
+ if (EnsureCapacity(newLen)) {
+ length_ = newLen;
+ }
+ }
+
+ size_t Length() const {
+ return length_;
+ }
+
+ uint8_t* data() {
+ return data_;
+ }
+
+ private:
+ bool EnsureCapacity(size_t capacity) {
+ if (capacity > capacity_) {
+ size_t newsize = capacity * 2;
+
+ uint8_t* data = static_cast<uint8_t*>(realloc(data_, newsize));
+
+ if (!data)
+ return false;
+
+ data_ = data;
+ capacity_ = newsize;
+ return true;
+ }
+ return true;
+ }
+
+ uint8_t* data_;
+ size_t capacity_;
+ size_t length_;
+};
+
+#endif //__BUFFEREDDATA_H__
--- /dev/null
+++ b/test/utils/HashFunctions.h
@@ -1,0 +1,15 @@
+#ifndef __HASHFUNCTIONS_H__
+#define __HASHFUNCTIONS_H__
+
+#include <openssl/sha.h>
+
+static bool CompareHash(const unsigned char* digest, const char* hashStr) {
+ char hashStrCmp[SHA_DIGEST_LENGTH * 2 + 1];
+ for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) {
+ sprintf(&hashStrCmp[i*2], "%.2x", digest[i]);
+ }
+ hashStrCmp[SHA_DIGEST_LENGTH * 2] = '\0';
+ return strncmp(hashStr, hashStrCmp, SHA_DIGEST_LENGTH * 2) == 0;
+}
+
+#endif //__HASHFUNCTIONS_H__