boB Rudis
4 years ago
131 changed files with 24866 additions and 398 deletions
@ -1,10 +1,5 @@ |
|||
# Generated by roxygen2: do not edit by hand |
|||
|
|||
export("%>%") |
|||
export(hash_compare) |
|||
export(hash_con) |
|||
export(hash_file) |
|||
export(hash_raw) |
|||
importFrom(Rcpp,sourceCpp) |
|||
importFrom(magrittr,"%>%") |
|||
useDynLib(ssdeepr, .registration = TRUE) |
|||
|
@ -1,43 +0,0 @@ |
|||
# Generated by using Rcpp::compileAttributes() -> do not edit by hand |
|||
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 |
|||
|
|||
#' Test whether an object is an external pointer |
|||
#' |
|||
#' @param x object to test |
|||
#' @keywords internal |
|||
check_is_xptr <- function(s) { |
|||
invisible(.Call(`_ssdeepr_check_is_xptr`, s)) |
|||
} |
|||
|
|||
#' Test whether an external pointer is null |
|||
#' |
|||
#' @param x object to test |
|||
#' @keywords internal |
|||
is_null_xptr_ <- function(s) { |
|||
.Call(`_ssdeepr_is_null_xptr_`, s) |
|||
} |
|||
|
|||
int_fuzzy_context <- function() { |
|||
.Call(`_ssdeepr_int_fuzzy_context`) |
|||
} |
|||
|
|||
int_fuzzy_update <- function(sctx, v) { |
|||
.Call(`_ssdeepr_int_fuzzy_update`, sctx, v) |
|||
} |
|||
|
|||
int_fuzzy_digest <- function(sctx) { |
|||
.Call(`_ssdeepr_int_fuzzy_digest`, sctx) |
|||
} |
|||
|
|||
int_hash_file <- function(path) { |
|||
.Call(`_ssdeepr_int_hash_file`, path) |
|||
} |
|||
|
|||
int_hash_buf <- function(v) { |
|||
.Call(`_ssdeepr_int_hash_buf`, v) |
|||
} |
|||
|
|||
int_hash_cmp <- function(h1, h2) { |
|||
.Call(`_ssdeepr_int_hash_cmp`, h1, h2) |
|||
} |
|||
|
@ -1,2 +0,0 @@ |
|||
#!/bin/sh |
|||
rm -f src/Makevars |
@ -1,75 +0,0 @@ |
|||
#!/bin/sh |
|||
# Anticonf (tm) script by Jeroen Ooms (2018) |
|||
# This script will query 'pkg-config' for the required cflags and ldflags. |
|||
# If pkg-config is unavailable or does not find the library, try setting |
|||
# INCLUDE_DIR and LIB_DIR manually via e.g: |
|||
# R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib' |
|||
|
|||
# Library settings |
|||
PKG_CONFIG_NAME="libfuzzy" |
|||
PKG_DEB_NAME="libfuzzy-dev" |
|||
PKG_RPM_NAME="libfuzzy-devel" |
|||
PKG_BREW_NAME="ssdeep" |
|||
PKG_TEST_HEADER="<fuzzy.h>" |
|||
PKG_LIBS_STATIC="-lfuzzy" |
|||
PKG_LIBS="-lfuzzy" |
|||
PKG_CFLAGS="" |
|||
|
|||
# Use pkg-config if available |
|||
if [ $(command -v pkg-config) ]; then |
|||
PKGCONFIG_CFLAGS=$(pkg-config --cflags --silence-errors ${PKG_CONFIG_NAME}) |
|||
PKGCONFIG_LIBS=$(pkg-config --libs ${PKG_CONFIG_NAME}) |
|||
fi |
|||
|
|||
# Note that cflags may be empty in case of success |
|||
if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then |
|||
echo "Found INCLUDE_DIR and/or LIB_DIR!" |
|||
PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS" |
|||
PKG_LIBS="-L$LIB_DIR $PKG_LIBS" |
|||
elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then |
|||
echo "Found pkg-config cflags and libs!" |
|||
PKG_CFLAGS=${PKGCONFIG_CFLAGS} |
|||
PKG_LIBS=${PKGCONFIG_LIBS} |
|||
elif [[ "$OSTYPE" == "darwin"* ]]; then |
|||
if [ $(command -v brew) ]; then |
|||
BREWDIR=$(brew --prefix) |
|||
else |
|||
curl -sfL "https://jeroen.github.io/autobrew/$PKG_BREW_NAME" > autobrew |
|||
source autobrew |
|||
fi |
|||
PKG_CFLAGS="-I$BREWDIR/opt/ssdeep/include" |
|||
PKG_LIBS="-L$BREWDIR/opt/ssdeep/lib -L$BREWDIR/opt/ssdeep/lib $PKG_LIBS" |
|||
fi |
|||
|
|||
# For debugging |
|||
echo "Using PKG_CFLAGS=$PKG_CFLAGS" |
|||
echo "Using PKG_LIBS=$PKG_LIBS" |
|||
|
|||
# Find compiler |
|||
CC=$(${R_HOME}/bin/R CMD config CC) |
|||
CFLAGS=$(${R_HOME}/bin/R CMD config CFLAGS) |
|||
CPPFLAGS=$(${R_HOME}/bin/R CMD config CPPFLAGS) |
|||
|
|||
# Test configuration |
|||
echo "#include $PKG_TEST_HEADER" | ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E -xc - >/dev/null 2>&1 || R_CONFIG_ERROR=1; |
|||
|
|||
# Customize the error |
|||
if [ $R_CONFIG_ERROR ]; then |
|||
echo "------------------------- ANTICONF ERROR ---------------------------" |
|||
echo "Configuration failed because $PKG_CONFIG_NAME was not found. Try installing:" |
|||
echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)" |
|||
echo " * rpm: $PKG_RPM_NAME (Fedora, EPEL)" |
|||
echo " * brew: $PKG_BREW_NAME (OSX)" |
|||
echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your" |
|||
echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config" |
|||
echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:" |
|||
echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'" |
|||
echo "--------------------------------------------------------------------" |
|||
exit 1; |
|||
fi |
|||
|
|||
# Write to Makevars |
|||
sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars |
|||
|
|||
# Success |
|||
exit 0 |
@ -1,15 +0,0 @@ |
|||
% Generated by roxygen2: do not edit by hand |
|||
% Please edit documentation in R/RcppExports.R |
|||
\name{check_is_xptr} |
|||
\alias{check_is_xptr} |
|||
\title{Test whether an object is an external pointer} |
|||
\usage{ |
|||
check_is_xptr(s) |
|||
} |
|||
\arguments{ |
|||
\item{x}{object to test} |
|||
} |
|||
\description{ |
|||
Test whether an object is an external pointer |
|||
} |
|||
\keyword{internal} |
@ -1,15 +0,0 @@ |
|||
% Generated by roxygen2: do not edit by hand |
|||
% Please edit documentation in R/RcppExports.R |
|||
\name{is_null_xptr_} |
|||
\alias{is_null_xptr_} |
|||
\title{Test whether an external pointer is null} |
|||
\usage{ |
|||
is_null_xptr_(s) |
|||
} |
|||
\arguments{ |
|||
\item{x}{object to test} |
|||
} |
|||
\description{ |
|||
Test whether an external pointer is null |
|||
} |
|||
\keyword{internal} |
@ -1,7 +1,15 @@ |
|||
PKG_CPPFLAGS=-I/usr/local/opt/ssdeep/include |
|||
PKG_LIBS=-L/usr/local/opt/ssdeep/lib -L/usr/local/opt/ssdeep/lib -lfuzzy |
|||
PKG_CPPFLAGS=-I. |
|||
CXX_STD = CXX11 |
|||
PKG_LIBS=-L. libffuzzypp.a |
|||
FUZZY_DIR=ffuzzypp-release-4.0.1 |
|||
|
|||
all: clean |
|||
all: libffuzzypp.a clean |
|||
|
|||
libffuzzypp.a: |
|||
cd lib/${FUZZY_DIR} && ./configure && make |
|||
cp lib/${FUZZY_DIR}/libffuzzypp.a ../.. |
|||
cp -r lib/${FUZZY_DIR}/ffuzzypp ../.. |
|||
cp lib/${FUZZY_DIR}/ffuzzy.hpp ../.. |
|||
|
|||
clean: |
|||
rm -Rf $(SHLIB) $(OBJECTS) |
|||
|
@ -1,7 +0,0 @@ |
|||
PKG_CPPFLAGS=@cflags@ |
|||
PKG_LIBS=@libs@ |
|||
|
|||
all: clean |
|||
|
|||
clean: |
|||
rm -Rf $(SHLIB) $(OBJECTS) |
@ -1,112 +0,0 @@ |
|||
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
|
|||
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
|
|||
|
|||
#include <Rcpp.h> |
|||
|
|||
using namespace Rcpp; |
|||
|
|||
// check_is_xptr
|
|||
void check_is_xptr(SEXP s); |
|||
RcppExport SEXP _ssdeepr_check_is_xptr(SEXP sSEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< SEXP >::type s(sSEXP); |
|||
check_is_xptr(s); |
|||
return R_NilValue; |
|||
END_RCPP |
|||
} |
|||
// is_null_xptr_
|
|||
SEXP is_null_xptr_(SEXP s); |
|||
RcppExport SEXP _ssdeepr_is_null_xptr_(SEXP sSEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< SEXP >::type s(sSEXP); |
|||
rcpp_result_gen = Rcpp::wrap(is_null_xptr_(s)); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
// int_fuzzy_context
|
|||
SEXP int_fuzzy_context(); |
|||
RcppExport SEXP _ssdeepr_int_fuzzy_context() { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
rcpp_result_gen = Rcpp::wrap(int_fuzzy_context()); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
// int_fuzzy_update
|
|||
SEXP int_fuzzy_update(SEXP sctx, std::vector < unsigned char > v); |
|||
RcppExport SEXP _ssdeepr_int_fuzzy_update(SEXP sctxSEXP, SEXP vSEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< SEXP >::type sctx(sctxSEXP); |
|||
Rcpp::traits::input_parameter< std::vector < unsigned char > >::type v(vSEXP); |
|||
rcpp_result_gen = Rcpp::wrap(int_fuzzy_update(sctx, v)); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
// int_fuzzy_digest
|
|||
String int_fuzzy_digest(SEXP sctx); |
|||
RcppExport SEXP _ssdeepr_int_fuzzy_digest(SEXP sctxSEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< SEXP >::type sctx(sctxSEXP); |
|||
rcpp_result_gen = Rcpp::wrap(int_fuzzy_digest(sctx)); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
// int_hash_file
|
|||
String int_hash_file(String path); |
|||
RcppExport SEXP _ssdeepr_int_hash_file(SEXP pathSEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< String >::type path(pathSEXP); |
|||
rcpp_result_gen = Rcpp::wrap(int_hash_file(path)); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
// int_hash_buf
|
|||
String int_hash_buf(std::vector < unsigned char > v); |
|||
RcppExport SEXP _ssdeepr_int_hash_buf(SEXP vSEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< std::vector < unsigned char > >::type v(vSEXP); |
|||
rcpp_result_gen = Rcpp::wrap(int_hash_buf(v)); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
// int_hash_cmp
|
|||
IntegerVector int_hash_cmp(String h1, String h2); |
|||
RcppExport SEXP _ssdeepr_int_hash_cmp(SEXP h1SEXP, SEXP h2SEXP) { |
|||
BEGIN_RCPP |
|||
Rcpp::RObject rcpp_result_gen; |
|||
Rcpp::RNGScope rcpp_rngScope_gen; |
|||
Rcpp::traits::input_parameter< String >::type h1(h1SEXP); |
|||
Rcpp::traits::input_parameter< String >::type h2(h2SEXP); |
|||
rcpp_result_gen = Rcpp::wrap(int_hash_cmp(h1, h2)); |
|||
return rcpp_result_gen; |
|||
END_RCPP |
|||
} |
|||
|
|||
static const R_CallMethodDef CallEntries[] = { |
|||
{"_ssdeepr_check_is_xptr", (DL_FUNC) &_ssdeepr_check_is_xptr, 1}, |
|||
{"_ssdeepr_is_null_xptr_", (DL_FUNC) &_ssdeepr_is_null_xptr_, 1}, |
|||
{"_ssdeepr_int_fuzzy_context", (DL_FUNC) &_ssdeepr_int_fuzzy_context, 0}, |
|||
{"_ssdeepr_int_fuzzy_update", (DL_FUNC) &_ssdeepr_int_fuzzy_update, 2}, |
|||
{"_ssdeepr_int_fuzzy_digest", (DL_FUNC) &_ssdeepr_int_fuzzy_digest, 1}, |
|||
{"_ssdeepr_int_hash_file", (DL_FUNC) &_ssdeepr_int_hash_file, 1}, |
|||
{"_ssdeepr_int_hash_buf", (DL_FUNC) &_ssdeepr_int_hash_buf, 1}, |
|||
{"_ssdeepr_int_hash_cmp", (DL_FUNC) &_ssdeepr_int_hash_cmp, 2}, |
|||
{NULL, NULL, 0} |
|||
}; |
|||
|
|||
RcppExport void R_init_ssdeepr(DllInfo *dll) { |
|||
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); |
|||
R_useDynamicSymbols(dll, FALSE); |
|||
} |
@ -0,0 +1,76 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
ffuzzy.hpp |
|||
Fuzzy hashing implementation |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_ROOT_FFUZZY_HPP |
|||
#define FFUZZYPP_ROOT_FFUZZY_HPP |
|||
|
|||
#include "ffuzzypp/utils/likely.hpp" |
|||
#include "ffuzzypp/utils/minmax.hpp" |
|||
#include "ffuzzypp/utils/safe_int.hpp" |
|||
#include "ffuzzypp/utils/static_assert_query.hpp" |
|||
#include "ffuzzypp/utils/numeric_digits.hpp" |
|||
#include "ffuzzypp/utils/type_modifier.hpp" |
|||
#include "ffuzzypp/utils/ranges.hpp" |
|||
#include "ffuzzypp/base64.hpp" |
|||
#include "ffuzzypp/context_hash.hpp" |
|||
#include "ffuzzypp/context_hash_fast.hpp" |
|||
#include "ffuzzypp/rolling_hash.hpp" |
|||
#include "ffuzzypp/rolling_hash_ssdeep.hpp" |
|||
#include "ffuzzypp/strings/position_array.hpp" |
|||
#include "ffuzzypp/strings/common_substr.hpp" |
|||
#include "ffuzzypp/strings/edit_dist.hpp" |
|||
#include "ffuzzypp/strings/terminators.hpp" |
|||
#include "ffuzzypp/strings/transform.hpp" |
|||
#include "ffuzzypp/strings/sequences.hpp" |
|||
#include "ffuzzypp/strings/nosequences.hpp" |
|||
#include "ffuzzypp/digest_blocksize.hpp" |
|||
#include "ffuzzypp/digest_data.hpp" |
|||
#include "ffuzzypp/digest_position_array_base.hpp" |
|||
#include "ffuzzypp/digest_comparison.hpp" |
|||
#include "ffuzzypp/digest_base.hpp" |
|||
#include "ffuzzypp/digest_position_array.hpp" |
|||
#include "ffuzzypp/digest.hpp" |
|||
#include "ffuzzypp/digest_filesize.hpp" |
|||
#include "ffuzzypp/digest_generator.hpp" |
|||
|
|||
#ifdef FFUZZYPP_COMPATIBILITY_SSDEEP_2_9 |
|||
#error Configuration by FFUZZYPP_COMPATIBILITY_SSDEEP_2_9 is now removed. Read README for alternative method. |
|||
#endif |
|||
#ifdef FFUZZYPP_COMPATIBILITY_SSDEEP |
|||
static_assert(ffuzzy::digest_params::max_blockhash_len == 64, |
|||
"Given parameter (digest_params::max_blockhash_len) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_params::max_blockhash_sequence == 3, |
|||
"Given parameter (digest_params::max_blockhash_sequence) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::blockhash_comparison_params::min_match_len == 7, |
|||
"Given parameter (blockhash_comparison_params::min_match_len) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::rolling_hash::window_size == 7, |
|||
"Given parameter (rolling_hash::window_size) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_blocksize::number_of_blockhashes == 31, |
|||
"Given parameter (digest_blocksize::number_of_blockhashes) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_blocksize::min_blocksize == 3, |
|||
"Given parameter (digest_blocksize::min_blocksize) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_filesize::min_supported_size == 4097, |
|||
"Given parameter (digest_filesize::min_supported_size) is not compatible with ssdeep."); |
|||
#endif |
|||
|
|||
#endif |
@ -0,0 +1,196 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
base64.hpp |
|||
Base64 utilities |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_BASE64_HPP |
|||
#define FFUZZYPP_BASE64_HPP |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class base64 |
|||
{ |
|||
private: |
|||
base64(void) = delete; |
|||
base64(const base64&) = delete; |
|||
|
|||
// Base64 table
|
|||
public: |
|||
static constexpr const char invalid_index = 64; |
|||
static constexpr const char values[64] = |
|||
{ |
|||
// This should be the same order of ordinal Base64
|
|||
// (as well as b64 variable in fuzzy.c)
|
|||
'A', 'B', 'C', 'D', 'E', 'F', 'G', |
|||
'H', 'I', 'J', 'K', 'L', 'M', 'N', |
|||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
|||
'V', 'W', 'X', 'Y', 'Z', |
|||
'a', 'b', 'c', 'd', 'e', 'f', 'g', |
|||
'h', 'i', 'j', 'k', 'l', 'm', 'n', |
|||
'o', 'p', 'q', 'r', 's', 't', 'u', |
|||
'v', 'w', 'x', 'y', 'z', |
|||
'0', '1', '2', '3', '4', |
|||
'5', '6', '7', '8', '9', |
|||
'+', '/' |
|||
}; |
|||
static_assert( |
|||
sizeof(values) == 64 && |
|||
values[ 0] == 'A' && |
|||
values[26] == 'a' && |
|||
values[52] == '0' && |
|||
values[62] == '+' && |
|||
values[63] == '/', |
|||
"minimum sanity check for Base64 failed."); |
|||
static_assert( |
|||
invalid_index < 0 || invalid_index >= 64, |
|||
"invalid_index is not out of range."); |
|||
|
|||
public: |
|||
// Utility to check whether given character is in Base64 charset
|
|||
static bool isbase64(char c) noexcept |
|||
{ |
|||
switch (c) |
|||
{ |
|||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': |
|||
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': |
|||
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': |
|||
case 'V': case 'W': case 'X': case 'Y': case 'Z': |
|||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': |
|||
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': |
|||
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': |
|||
case 'v': case 'w': case 'x': case 'y': case 'z': |
|||
case '0': case '1': case '2': case '3': case '4': |
|||
case '5': case '6': case '7': case '8': case '9': |
|||
case '+': case '/': |
|||
return true; |
|||
default: |
|||
return false; |
|||
} |
|||
} |
|||
// Utility to convert character to Base64 index
|
|||
static char toindex(char c) noexcept |
|||
{ |
|||
switch (c) |
|||
{ |
|||
case 'A': return 0; |
|||
case 'B': return 1; |
|||
case 'C': return 2; |
|||
case 'D': return 3; |
|||
case 'E': return 4; |
|||
case 'F': return 5; |
|||
case 'G': return 6; |
|||
case 'H': return 7; |
|||
case 'I': return 8; |
|||
case 'J': return 9; |
|||
case 'K': return 10; |
|||
case 'L': return 11; |
|||
case 'M': return 12; |
|||
case 'N': return 13; |
|||
case 'O': return 14; |
|||
case 'P': return 15; |
|||
case 'Q': return 16; |
|||
case 'R': return 17; |
|||
case 'S': return 18; |
|||
case 'T': return 19; |
|||
case 'U': return 20; |
|||
case 'V': return 21; |
|||
case 'W': return 22; |
|||
case 'X': return 23; |
|||
case 'Y': return 24; |
|||
case 'Z': return 25; |
|||
case 'a': return 26; |
|||
case 'b': return 27; |
|||
case 'c': return 28; |
|||
case 'd': return 29; |
|||
case 'e': return 30; |
|||
case 'f': return 31; |
|||
case 'g': return 32; |
|||
case 'h': return 33; |
|||
case 'i': return 34; |
|||
case 'j': return 35; |
|||
case 'k': return 36; |
|||
case 'l': return 37; |
|||
case 'm': return 38; |
|||
case 'n': return 39; |
|||
case 'o': return 40; |
|||
case 'p': return 41; |
|||
case 'q': return 42; |
|||
case 'r': return 43; |
|||
case 's': return 44; |
|||
case 't': return 45; |
|||
case 'u': return 46; |
|||
case 'v': return 47; |
|||
case 'w': return 48; |
|||
case 'x': return 49; |
|||
case 'y': return 50; |
|||
case 'z': return 51; |
|||
case '0': return 52; |
|||
case '1': return 53; |
|||
case '2': return 54; |
|||
case '3': return 55; |
|||
case '4': return 56; |
|||
case '5': return 57; |
|||
case '6': return 58; |
|||
case '7': return 59; |
|||
case '8': return 60; |
|||
case '9': return 61; |
|||
case '+': return 62; |
|||
case '/': return 63; |
|||
default: return invalid_index; |
|||
} |
|||
} |
|||
|
|||
// Base64 transformation (from index to actual character)
|
|||
public: |
|||
class transform_to_b64 |
|||
{ |
|||
private: |
|||
transform_to_b64(void) = delete; |
|||
transform_to_b64(const transform_to_b64&) = delete; |
|||
public: |
|||
static constexpr char transform(char ch) noexcept |
|||
{ |
|||
return base64::values[static_cast<unsigned char>(ch)]; |
|||
} |
|||
}; |
|||
|
|||
// Base64 reverse-transformation (from Base64 character to index)
|
|||
public: |
|||
class transform_from_b64 |
|||
{ |
|||
private: |
|||
transform_from_b64(void) = delete; |
|||
transform_from_b64(const transform_from_b64&) = delete; |
|||
public: |
|||
static char transform(char ch) noexcept |
|||
{ |
|||
return toindex(ch); |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
#ifdef FFUZZYPP_DECLARATIONS |
|||
constexpr const char base64::values[64]; |
|||
#endif |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
context_hash.hpp |
|||
Context computation (non-rolling) hash implementation |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_CONTEXT_HASH_HPP |
|||
#define FFUZZYPP_CONTEXT_HASH_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class context_hash |
|||
{ |
|||
private: |
|||
// Don't use UINT32_C not to define __STDC_CONSTANT_MACROS in ffuzzy++
|
|||
static constexpr const uint_least32_t hash_init = uint_least32_t(0x28021967ul); |
|||
static constexpr const uint_least32_t hash_prime = uint_least32_t(0x01000193ul); |
|||
private: |
|||
uint_least32_t h; |
|||
public: |
|||
static constexpr uint_least32_t initial_state(void) noexcept |
|||
{ |
|||
return hash_init; |
|||
} |
|||
void reset(void) noexcept |
|||
{ |
|||
h = initial_state(); |
|||
} |
|||
static constexpr uint_least32_t next_state(uint_least32_t h, unsigned char c) noexcept |
|||
{ |
|||
return ((h * hash_prime) ^ uint_least32_t(c)) & uint_least32_t(0xfffffffful); |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
h = next_state(h, c); |
|||
} |
|||
uint_least32_t sum(void) const noexcept |
|||
{ |
|||
return h; |
|||
} |
|||
public: |
|||
char sum_in_base64(void) const noexcept |
|||
{ |
|||
// sum for Base64 (returns Base64 index)
|
|||
return static_cast<char>(h & 0x3f); |
|||
} |
|||
public: |
|||
context_hash(void) noexcept = default; // initialize to undefined state
|
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,480 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
context_hash_fast.hpp |
|||
Fast implementation of context_hash (only lowest 6-bits) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_CONTEXT_HASH_FAST_HPP |
|||
#define FFUZZYPP_CONTEXT_HASH_FAST_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
#include "context_hash.hpp" |
|||
#include "utils/static_assert_query.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class context_hash_fast |
|||
{ |
|||
private: |
|||
static constexpr const char hash_init = static_cast<char>(context_hash::initial_state() & 0x3f); |
|||
static constexpr const char table_translate[64][64] = |
|||
{ |
|||
{ // 0x00
|
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
}, |
|||
{ // 0x01
|
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
}, |
|||
{ // 0x02
|
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
}, |
|||
{ // 0x03
|
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
}, |
|||
{ // 0x04
|
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
}, |
|||
{ // 0x05
|
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
}, |
|||
{ // 0x06
|
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
}, |
|||
{ // 0x07
|
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
}, |
|||
{ // 0x08
|
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
}, |
|||
{ // 0x09
|
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
}, |
|||
{ // 0x0a
|
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
}, |
|||
{ // 0x0b
|
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
}, |
|||
{ // 0x0c
|
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
}, |
|||
{ // 0x0d
|
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
}, |
|||
{ // 0x0e
|
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
}, |
|||
{ // 0x0f
|
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
}, |
|||
{ // 0x10
|
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
}, |
|||
{ // 0x11
|
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
}, |
|||
{ // 0x12
|
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
}, |
|||
{ // 0x13
|
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
}, |
|||
{ // 0x14
|
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
}, |
|||
{ // 0x15
|
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
}, |
|||
{ // 0x16
|
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
}, |
|||
{ // 0x17
|
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
}, |
|||
{ // 0x18
|
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
}, |
|||
{ // 0x19
|
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
}, |
|||
{ // 0x1a
|
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
}, |
|||
{ // 0x1b
|
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
}, |
|||
{ // 0x1c
|
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
}, |
|||
{ // 0x1d
|
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
}, |
|||
{ // 0x1e
|
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
}, |
|||
{ // 0x1f
|
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
}, |
|||
{ // 0x20
|
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
}, |
|||
{ // 0x21
|
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
}, |
|||
{ // 0x22
|
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
}, |
|||
{ // 0x23
|
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
}, |
|||
{ // 0x24
|
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
}, |
|||
{ // 0x25
|
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
}, |
|||
{ // 0x26
|
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
}, |
|||
{ // 0x27
|
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
}, |
|||
{ // 0x28
|
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
}, |
|||
{ // 0x29
|
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
}, |
|||
{ // 0x2a
|
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
}, |
|||
{ // 0x2b
|
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
}, |
|||
{ // 0x2c
|
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
}, |
|||
{ // 0x2d
|
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
}, |
|||
{ // 0x2e
|
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
}, |
|||
{ // 0x2f
|
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
}, |
|||
{ // 0x30
|
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
}, |
|||
{ // 0x31
|
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
}, |
|||
{ // 0x32
|
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
}, |
|||
{ // 0x33
|
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
}, |
|||
{ // 0x34
|
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
}, |
|||
{ // 0x35
|
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
}, |
|||
{ // 0x36
|
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
}, |
|||
{ // 0x37
|
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
}, |
|||
{ // 0x38
|
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
}, |
|||
{ // 0x39
|
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
}, |
|||
{ // 0x3a
|
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
}, |
|||
{ // 0x3b
|
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
}, |
|||
{ // 0x3c
|
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
}, |
|||
{ // 0x3d
|
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
}, |
|||
{ // 0x3e
|
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
}, |
|||
{ // 0x3f
|
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
}, |
|||
}; |
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
template <uintmax_t i> |
|||
struct translate_check |
|||
{ |
|||
template <uintmax_t j> |
|||
struct translate_check_2 |
|||
{ |
|||
static constexpr const bool value = |
|||
table_translate[i][j] == (context_hash::next_state(i, j) & 0x3f); |
|||
}; |
|||
static constexpr const bool value = |
|||
static_assert_query::is_all<translate_check_2, 64>::value; |
|||
}; |
|||
static_assert(static_assert_query::is_all<translate_check, 64>::value, |
|||
"table_translate is not equivalent to actual context_hash translation."); |
|||
#endif |
|||
|
|||
private: |
|||
char h; |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
h = hash_init; |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
h = table_translate[static_cast<unsigned char>(h)][c & 0x3f]; |
|||
} |
|||
char sum_in_base64(void) const noexcept |
|||
{ |
|||
// sum for Base64 (returns Base64 index)
|
|||
return h; |
|||
} |
|||
public: |
|||
context_hash_fast(void) noexcept = default; // initialize to undefined state
|
|||
}; |
|||
|
|||
|
|||
#ifdef FFUZZYPP_DECLARATIONS |
|||
constexpr const char context_hash_fast::table_translate[64][64]; |
|||
#endif |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,495 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest.hpp |
|||
Fuzzy digest (wrapper with converters) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_HPP |
|||
#define FFUZZYPP_DIGEST_HPP |
|||
|
|||
#include <algorithm> |
|||
#include <functional> |
|||
#include <string> |
|||
#include <type_traits> |
|||
|
|||
#include "digest_data.hpp" |
|||
#include "digest_base.hpp" |
|||
#include "utils/type_modifier.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
// Wrapper class
|
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> class digest; |
|||
|
|||
// Digest (alphabet restricted; short; normalized)
|
|||
template <> |
|||
class digest<true, true, true> |
|||
: public digest_base<true, true, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, true, true>(other) {} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, true, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, true, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet restricted; short; unnormalized)
|
|||
template <> |
|||
class digest<true, true, false> |
|||
: public digest_base<true, true, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, true, false>(other) {} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
digest_data<true, true>::operator=(other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, true, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, true, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
digest_data<true, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet restricted; long; normalized)
|
|||
template <> |
|||
class digest<true, false, true> |
|||
: public digest_base<true, false, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, false, true>(other) {} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, false, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, false, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet restricted; long; unnormalized)
|
|||
template <> |
|||
class digest<true, false, false> |
|||
: public digest_base<true, false, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, false, false>(other) {} |
|||
digest(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
digest_data<true, false>::operator=(other); |
|||
} |
|||
digest(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, false, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, false, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
digest_data<true, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; short; normalized)
|
|||
template <> |
|||
class digest<false, true, true> |
|||
: public digest_base<false, true, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, true, true>(other) {} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, true, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, true, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; short; unnormalized)
|
|||
template <> |
|||
class digest<false, true, false> |
|||
: public digest_base<false, true, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, true, false>(other) {} |
|||
digest(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
digest_data<false, true>::operator=(other); |
|||
} |
|||
digest(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, true, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, true, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
digest_data<false, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; long; normalized)
|
|||
template <> |
|||
class digest<false, false, true> |
|||
: public digest_base<false, false, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, false, true>(other) {} |
|||
digest(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, false, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, false, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; long; unnormalized)
|
|||
template <> |
|||
class digest<false, false, false> |
|||
: public digest_base<false, false, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, false, false>(other) {} |
|||
digest(const digest<false, false, true>& other) noexcept |
|||
{ |
|||
digest_data<false, false>::operator=(other); |
|||
} |
|||
digest(const digest<false, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<true, false, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, false, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, false, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, false, true>& other) noexcept |
|||
{ |
|||
digest_data<false, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Typedefs for digest specializations
|
|||
typedef digest<false, true, true> digest_t; |
|||
typedef digest<false, true, false> digest_unorm_t; |
|||
typedef digest<false, false, true> digest_long_t; |
|||
typedef digest<false, false, false> digest_long_unorm_t; |
|||
typedef digest< true, true, true> digest_ra_t; |
|||
typedef digest< true, true, false> digest_ra_unorm_t; |
|||
typedef digest< true, false, true> digest_ra_long_t; |
|||
typedef digest< true, false, false> digest_ra_long_unorm_t; |
|||
|
|||
|
|||
|
|||
/*
|
|||
Type modifiers |
|||
*/ |
|||
namespace internal |
|||
{ |
|||
template <typename T> |
|||
struct digest_traits |
|||
{ |
|||
static constexpr const bool is_valid = false; |
|||
}; |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
struct digest_traits<digest_base<IsAlphabetRestricted, IsShort, IsNormalized>> |
|||
{ |
|||
static constexpr const bool is_valid = true; |
|||
typedef digest_base<IsAlphabetRestricted, IsShort, true> norm_type; |
|||
typedef digest_base<IsAlphabetRestricted, IsShort, false> unorm_type; |
|||
typedef digest_base<IsAlphabetRestricted, true, IsNormalized> short_type; |
|||
typedef digest_base<IsAlphabetRestricted, false, IsNormalized> long_type; |
|||
typedef digest_base< true, IsShort, IsNormalized> ra_type; |
|||
typedef digest_base<false, IsShort, IsNormalized> non_ra_type; |
|||
}; |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
struct digest_traits<digest<IsAlphabetRestricted, IsShort, IsNormalized>> |
|||
{ |
|||
static constexpr const bool is_valid = true; |
|||
typedef digest<IsAlphabetRestricted, IsShort, true> norm_type; |
|||
typedef digest<IsAlphabetRestricted, IsShort, false> unorm_type; |
|||
typedef digest<IsAlphabetRestricted, true, IsNormalized> short_type; |
|||
typedef digest<IsAlphabetRestricted, false, IsNormalized> long_type; |
|||
typedef digest< true, IsShort, IsNormalized> ra_type; |
|||
typedef digest<false, IsShort, IsNormalized> non_ra_type; |
|||
}; |
|||
template <typename T> |
|||
struct digest_alt_type_selector |
|||
{ |
|||
private: |
|||
typedef digest_traits<typename std::remove_cv<T>::type> traits_type; |
|||
static_assert(traits_type::is_valid, "You must give correct type to retrieve alternative types."); |
|||
public: |
|||
typedef typename type_mod::cv_match<T, typename traits_type::norm_type>::type norm_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::unorm_type>::type unorm_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::short_type>::type short_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::long_type>::type long_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::ra_type>::type ra_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::non_ra_type>::type non_ra_type; |
|||
}; |
|||
} |
|||
template <typename T> using digest_to_unorm = typename internal::digest_alt_type_selector<T>::unorm_type; |
|||
template <typename T> using digest_to_norm = typename internal::digest_alt_type_selector<T>::norm_type; |
|||
template <typename T> using digest_to_short = typename internal::digest_alt_type_selector<T>::short_type; |
|||
template <typename T> using digest_to_long = typename internal::digest_alt_type_selector<T>::long_type; |
|||
template <typename T> using digest_to_ra = typename internal::digest_alt_type_selector<T>::ra_type; |
|||
template <typename T> using digest_to_non_ra = typename internal::digest_alt_type_selector<T>::non_ra_type; |
|||
|
|||
|
|||
/*
|
|||
We expect following properties for each digest types: |
|||
|
|||
* is a trivially default constructible type |
|||
* is a standard-layout type |
|||
|
|||
Digest types are: |
|||
|
|||
* digest_data<IsAlphabetRestricted, IsShort> |
|||
* digest<IsAlphabetRestricted, IsShort, true> |
|||
* digest<IsAlphabetRestricted, IsShort, false> |
|||
|
|||
We also expect that these types are nearly equivalent. |
|||
*/ |
|||
#ifdef FFUZZYPP_LOCAL_CHK |
|||
#error do not define FFUZZYPP_LOCAL_CHK |
|||
#endif |
|||
#ifdef FFUZZYPP_LOCAL_CHK1 |
|||
#error do not define FFUZZYPP_LOCAL_CHK1 |
|||
#endif |
|||
#define FFUZZYPP_LOCAL_CHK1(IsAlphabetRestricted, IsShort, IsNormalized) \ |
|||
static_assert(std::is_trivially_default_constructible<digest<IsAlphabetRestricted, IsShort, IsNormalized>>::value, \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", " #IsNormalized "> must be a trivially default constructible type."); \ |
|||
static_assert(std::is_standard_layout<digest<IsAlphabetRestricted, IsShort, IsNormalized>>::value, \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", " #IsNormalized "> must be a standard-layout type."); \ |
|||
static_assert(std::is_base_of<digest_data<IsAlphabetRestricted, IsShort>, digest<IsAlphabetRestricted, IsShort, IsNormalized>>::value, \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort ">, digest<" #IsAlphabetRestricted ", " #IsShort ", true> and " \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", false> must be nearly equivalent."); \ |
|||
static_assert(sizeof(digest_data<IsAlphabetRestricted, IsShort>) == sizeof(digest<IsAlphabetRestricted, IsShort, IsNormalized>), \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort ">, digest<" #IsAlphabetRestricted ", " #IsShort ", true> and " \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", false> must be nearly equivalent.") |
|||
#define FFUZZYPP_LOCAL_CHK(IsAlphabetRestricted, IsShort) \ |
|||
static_assert(std::is_trivially_default_constructible<digest_data<IsAlphabetRestricted, IsShort>>::value, \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort "> must be a trivially default constructible type."); \ |
|||
static_assert(std::is_standard_layout<digest_data<IsAlphabetRestricted, IsShort>>::value, \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort "> must be a standard-layout type."); \ |
|||
FFUZZYPP_LOCAL_CHK1(IsAlphabetRestricted, IsShort, true); \ |
|||
FFUZZYPP_LOCAL_CHK1(IsAlphabetRestricted, IsShort, false) |
|||
FFUZZYPP_LOCAL_CHK(true, true); |
|||
FFUZZYPP_LOCAL_CHK(true, false); |
|||
FFUZZYPP_LOCAL_CHK(false, true); |
|||
FFUZZYPP_LOCAL_CHK(false, false); |
|||
#undef FFUZZYPP_LOCAL_CHK |
|||
#undef FFUZZYPP_LOCAL_CHK1 |
|||
|
|||
} |
|||
|
|||
|
|||
// Specialization of standard hash and swap
|
|||
namespace std |
|||
{ |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
struct hash<ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>> |
|||
{ |
|||
size_t operator()(const ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>& value) const |
|||
{ |
|||
return value.hash(); |
|||
} |
|||
}; |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
inline void swap( |
|||
ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>& a, |
|||
ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>& b |
|||
) noexcept |
|||
{ |
|||
ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>::swap(a, b); |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,254 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_base.hpp |
|||
Fuzzy digest (wrapper with comparison methods) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_BASE_HPP |
|||
#define FFUZZYPP_DIGEST_BASE_HPP |
|||
|
|||
#include <string> |
|||
|
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "digest_comparison.hpp" |
|||
#include "utils/type_modifier.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> class digest_base; |
|||
|
|||
// Normalized form of digest (with specialized/fast comparison)
|
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
class digest_base<IsAlphabetRestricted, IsShort, true> |
|||
: public digest_data<IsAlphabetRestricted, IsShort> |
|||
{ |
|||
public: |
|||
digest_base(void) noexcept = default; // initialize to undefined state
|
|||
digest_base(const digest_base& other) noexcept : digest_data<IsAlphabetRestricted, IsShort>(other) {} |
|||
const digest_base& operator=(const digest_base& other) noexcept |
|||
{ |
|||
digest_data<IsAlphabetRestricted, IsShort>::operator=(other); |
|||
return *this; |
|||
} |
|||
public: |
|||
explicit digest_base(const char* str) noexcept(false) |
|||
{ |
|||
if (!digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(*this, str)) |
|||
throw digest_parse_error(); |
|||
} |
|||
explicit digest_base(const std::string& str) |
|||
: digest_base(str.c_str()) {} |
|||
static bool parse_normalized(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(digest, str); |
|||
} |
|||
static bool parse(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(digest, str); |
|||
} |
|||
public: |
|||
bool is_valid(void) const noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::is_valid() && this->template is_normalized(); |
|||
} |
|||
|
|||
// Comparison
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_identical( |
|||
const digest_base& value |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_identical(value); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base& other) const noexcept |
|||
{ |
|||
return compare<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_identical(void) const noexcept |
|||
{ |
|||
return compare_identical<Version>(*this); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_eq<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_lt(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(*this, other); |
|||
} |
|||
|
|||
// Comparison (on different digests)
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_diff(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_diff(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq_diff(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_eq_diff<Version>(*this, other); |
|||
} |
|||
}; |
|||
|
|||
// Unnormalized form of digest (with normalization ability and slow comparison)
|
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
class digest_base<IsAlphabetRestricted, IsShort, false> |
|||
: public digest_data<IsAlphabetRestricted, IsShort> |
|||
{ |
|||
public: |
|||
digest_base(void) noexcept = default; // initialize to undefined state
|
|||
digest_base(const digest_base& other) noexcept : digest_data<IsAlphabetRestricted, IsShort>(other) {} |
|||
digest_base(const digest_base<IsAlphabetRestricted, IsShort, true>& other) noexcept : digest_data<IsAlphabetRestricted, IsShort>(other) {} |
|||
const digest_base& operator=(const digest_base& other) noexcept |
|||
{ |
|||
digest_data<IsAlphabetRestricted, IsShort>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest_base& operator=(const digest_base<IsAlphabetRestricted, IsShort, true>& other) noexcept |
|||
{ |
|||
digest_data<IsAlphabetRestricted, IsShort>::operator=(other); |
|||
return *this; |
|||
} |
|||
public: |
|||
explicit digest_base(const char* str) noexcept(false) |
|||
{ |
|||
if (!digest_data<IsAlphabetRestricted, IsShort>::parse(*this, str)) |
|||
throw digest_parse_error(); |
|||
} |
|||
explicit digest_base(const std::string& str) |
|||
: digest_base(str.c_str()) {} |
|||
static bool parse_normalized(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(digest, str); |
|||
} |
|||
static bool parse(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse(digest, str); |
|||
} |
|||
public: |
|||
digest_base<IsAlphabetRestricted, IsShort, true> to_normalized(void) const noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>:: |
|||
template normalize<digest_base<IsAlphabetRestricted, IsShort, true>>(*this); |
|||
} |
|||
explicit operator digest_base<IsAlphabetRestricted, IsShort, true>(void) const noexcept { return to_normalized(); } |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_unnormalized(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base& other) const noexcept { return compare<Version>(*this, other); } |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,255 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_blocksize.hpp |
|||
Fuzzy digest block size utilities |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_BLOCKSIZE_HPP |
|||
#define FFUZZYPP_DIGEST_BLOCKSIZE_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include <limits> |
|||
|
|||
#include "utils/safe_int.hpp" |
|||
#include "utils/static_assert_query.hpp" |
|||
#include "utils/numeric_digits.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
// Block size type for fuzzy digest
|
|||
typedef uint_least32_t digest_blocksize_t; |
|||
|
|||
// Block size utilities
|
|||
class digest_blocksize |
|||
{ |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<digest_blocksize_t>() && |
|||
std::numeric_limits<digest_blocksize_t>::max() >= 0xfffffffful, |
|||
"digest_blocksize_t must be an unsigned integral type which can represent all 32-bit values." |
|||
); |
|||
private: |
|||
digest_blocksize(void) = delete; |
|||
digest_blocksize(const digest_blocksize&) = delete; |
|||
|
|||
// Parameters
|
|||
public: |
|||
static constexpr const unsigned number_of_blockhashes = 31; |
|||
static constexpr const digest_blocksize_t min_blocksize = 3; |
|||
static constexpr const digest_blocksize_t max_blocksize = min_blocksize << (number_of_blockhashes - 1); |
|||
static_assert(min_blocksize != 0, "min_blocksize must not be zero."); |
|||
static_assert(number_of_blockhashes != 0, "number_of_blockhashes must not be zero."); |
|||
static_assert(number_of_blockhashes <= 32, "number_of_blockhashes must be less than 32."); |
|||
static_assert(min_blocksize <= 0xfffffffful, "min_blocksize must be less than 2^32."); |
|||
static_assert( |
|||
safe_int::safe_lshift< |
|||
safe_int::uvalue<digest_blocksize_t, min_blocksize>, |
|||
safe_int::uvalue<unsigned, number_of_blockhashes - 1> |
|||
>::is_valid && |
|||
(min_blocksize << (number_of_blockhashes - 1)) <= 0xfffffffful, |
|||
"(min_blocksize << (number_of_blockhashes - 1)) must be less than 2^32."); |
|||
|
|||
// Maximum characters required to represent all "natural" block sizes
|
|||
// (excluding '\0' character at the end)
|
|||
public: |
|||
static constexpr const size_t max_natural_digits = |
|||
numeric_digits::in_decimal(max_blocksize); |
|||
|
|||
// Block size naturality
|
|||
// (whether this is possibly generated by ssdeep)
|
|||
public: |
|||
static bool is_natural(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
if (blocksize < min_blocksize) |
|||
return false; |
|||
if (blocksize > max_blocksize) |
|||
return false; |
|||
while (blocksize != min_blocksize) |
|||
{ |
|||
if (blocksize % 2 != 0) |
|||
return false; |
|||
blocksize /= 2; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Utility to prevent arithmetic overflow
|
|||
public: |
|||
static constexpr bool is_safe_to_double(digest_blocksize_t value) noexcept |
|||
{ |
|||
return value <= (std::numeric_limits<digest_blocksize_t>::max() / 2); |
|||
} |
|||
|
|||
/*
|
|||
"Near" relations |
|||
|
|||
fuzzy digests are compared against other if their block sizes are "near". |
|||
There are three such cases (if there is no arithmetic overflow). |
|||
|
|||
Case 1: a == b (eq) |
|||
Digest A : 3:xxxxxxx:yyyyyyy |
|||
Digest A : 3:xxxxxxx:yyyyyyy |
|||
| ~~~~~~~ ~~~~~~~ |
|||
| (both block hashes are compared and the maximum score is chosen) |
|||
Case 2: a * 2 == b (implies a < b; lt) |
|||
Digest A : 3:xxxxxxx:yyyyyyy |
|||
Digest B : 6: yyyyyyy:zzzzzzz |
|||
| ~~~~~~~ |
|||
| (block hash in common block size is compared) |
|||
Case 3: a == b * 2 (implies a > b; gt) |
|||
Digest A : 6: yyyyyyy:zzzzzzz |
|||
Digest B : 3:xxxxxxx:yyyyyyy |
|||
| ~~~~~~~ |
|||
| (block hash in common block size is compared) |
|||
*/ |
|||
public: |
|||
static constexpr bool is_near_eq(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return a == b; |
|||
} |
|||
static constexpr bool is_near_lt(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return a != b && is_safe_to_double(a) && a * 2 == b; |
|||
} |
|||
static constexpr bool is_near_gt(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return a != b && a % 2 == 0 && a / 2 == b; |
|||
} |
|||
static constexpr bool is_near_leq(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return is_near_eq(a, b) || is_near_lt(a, b); |
|||
} |
|||
static constexpr bool is_near(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return is_near_eq(a, b) || is_near_lt(a, b) || is_near_gt(a, b); |
|||
} |
|||
|
|||
// Utility to convert block size index to a natural block size
|
|||
public: |
|||
static constexpr digest_blocksize_t at(unsigned index) noexcept |
|||
{ |
|||
return min_blocksize << index; |
|||
} |
|||
|
|||
// Utility to convert natural block size to corresponding block size index
|
|||
private: |
|||
template <bool UseMagic, typename Tdummy = void> class natural_to_index_impl; |
|||
// Implementation by magic table
|
|||
// (not general [depends on ssdeep parameters] but possibly fast)
|
|||
static constexpr const digest_blocksize_t nti_magic_mul = digest_blocksize_t(0x017713caul); |
|||
static constexpr const unsigned nti_magic_table[31] = |
|||
{ |
|||
0x00, 0x01, 0x02, 0x06, 0x03, 0x0b, 0x07, 0x10, |
|||
0x04, 0x0e, 0x0c, 0x18, 0x08, 0x15, 0x11, 0x1a, |
|||
0x1e, 0x05, 0x0a, 0x0f, 0x0d, 0x17, 0x14, 0x19, |
|||
0x1d, 0x09, 0x16, 0x13, 0x1c, 0x12, 0x1b, |
|||
}; |
|||
template <typename Tdummy> |
|||
class natural_to_index_impl<true, Tdummy> |
|||
{ |
|||
public: |
|||
static constexpr unsigned natural_to_index_unsafe(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return nti_magic_table[((blocksize * nti_magic_mul) >> 27) & 0x1f]; |
|||
} |
|||
private: |
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
template <uintmax_t i> |
|||
struct seq_check |
|||
{ |
|||
static constexpr const bool value = |
|||
natural_to_index_unsafe(min_blocksize << i) == i; |
|||
}; |
|||
static_assert(static_assert_query::is_all<seq_check, number_of_blockhashes>::value, |
|||
"magic table is not constructed correctly."); |
|||
#endif |
|||
}; |
|||
// Implementation by De Brujin sequence
|
|||
// (general but requires a division to make index_from work)
|
|||
static constexpr const digest_blocksize_t nti_debrujin_mul = digest_blocksize_t(0x077cb531ul); |
|||
static constexpr const unsigned nti_debrujin_table[32] = |
|||
{ |
|||
0x00, 0x01, 0x1c, 0x02, 0x1d, 0x0e, 0x18, 0x03, |
|||
0x1e, 0x16, 0x14, 0x0f, 0x19, 0x11, 0x04, 0x08, |
|||
0x1f, 0x1b, 0x0d, 0x17, 0x15, 0x13, 0x10, 0x07, |
|||
0x1a, 0x0c, 0x12, 0x06, 0x0b, 0x05, 0x0a, 0x09, |
|||
}; |
|||
template <typename Tdummy> |
|||
class natural_to_index_impl<false, Tdummy> |
|||
{ |
|||
public: |
|||
static constexpr unsigned natural_to_index_unsafe(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return nti_debrujin_table[((blocksize / min_blocksize * nti_debrujin_mul) >> 27) & 0x1f]; |
|||
} |
|||
private: |
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
template <uintmax_t i> |
|||
struct seq_check |
|||
{ |
|||
static constexpr const bool value = |
|||
natural_to_index_unsafe(min_blocksize << i) == i; |
|||
}; |
|||
static_assert(static_assert_query::is_all<seq_check, number_of_blockhashes>::value, |
|||
"De Brujin sequence is not constructed correctly."); |
|||
#endif |
|||
}; |
|||
static constexpr const bool nti_is_magic_available = |
|||
min_blocksize == 3 && number_of_blockhashes <= 31; |
|||
public: |
|||
template <bool UseDebrujin = false> |
|||
static constexpr unsigned natural_to_index_unsafe(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return natural_to_index_impl< |
|||
nti_is_magic_available && !UseDebrujin |
|||
>::natural_to_index_unsafe(blocksize); |
|||
} |
|||
template <bool UseDebrujin = false> |
|||
static unsigned natural_to_index(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_natural(blocksize)); |
|||
#endif |
|||
return natural_to_index_unsafe<UseDebrujin>(blocksize); |
|||
} |
|||
}; |
|||
|
|||
|
|||
#ifdef FFUZZYPP_DECLARATIONS |
|||
constexpr const unsigned digest_blocksize::nti_magic_table[31]; |
|||
constexpr const unsigned digest_blocksize::nti_debrujin_table[32]; |
|||
#endif |
|||
|
|||
} |
|||
|
|||
#endif |
File diff suppressed because it is too large
@ -0,0 +1,649 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_data.hpp |
|||
Fuzzy digest (data and basic portions) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_DATA_HPP |
|||
#define FFUZZYPP_DIGEST_DATA_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdlib> |
|||
#include <cstring> |
|||
|
|||
#include <functional> |
|||
#include <limits> |
|||
#include <string> |
|||
#include <type_traits> |
|||
#include <utility> |
|||
|
|||
#include <errno.h> |
|||
|
|||
#include "base64.hpp" |
|||
#include "digest_blocksize.hpp" |
|||
#include "strings/sequences.hpp" |
|||
#include "strings/nosequences.hpp" |
|||
#include "utils/safe_int.hpp" |
|||
#include "utils/numeric_digits.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
typedef size_t blockhash_len_t; |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<blockhash_len_t>(), |
|||
"blockhash_len_t must be an unsigned integral type."); |
|||
|
|||
struct digest_parse_error {}; |
|||
|
|||
|
|||
class digest_params |
|||
{ |
|||
private: |
|||
digest_params(void) = delete; |
|||
digest_params(const digest_params&) = delete; |
|||
|
|||
// Common digest parameters (for both short and long forms)
|
|||
public: |
|||
static constexpr const blockhash_len_t max_blockhash_len = 64; |
|||
static constexpr const blockhash_len_t max_blockhash_sequence = 3; |
|||
static_assert(max_blockhash_len >= 2, "max_blockhash_len must be 2 or greater."); |
|||
static_assert(max_blockhash_sequence != 0, "max_blockhash_sequence must not be zero."); |
|||
static_assert(safe_int::uvalue<size_t, max_blockhash_len>::is_valid, |
|||
"max_blockhash_len must be in range of size_t."); |
|||
static_assert(safe_int::uvalue<size_t, max_blockhash_sequence>::is_valid, |
|||
"max_blockhash_sequence must be in range of size_t."); |
|||
}; |
|||
|
|||
|
|||
template <bool IsAlphabetRestricted> class digest_data_transformation; |
|||
|
|||
template<> |
|||
class digest_data_transformation<true> |
|||
{ |
|||
private: |
|||
digest_data_transformation(void) = delete; |
|||
digest_data_transformation(const digest_data_transformation&) = delete; |
|||
public: |
|||
typedef base64::transform_from_b64 input_type; |
|||
typedef base64::transform_to_b64 output_type; |
|||
}; |
|||
|
|||
template<> |
|||
class digest_data_transformation<false> |
|||
{ |
|||
private: |
|||
digest_data_transformation(void) = delete; |
|||
digest_data_transformation(const digest_data_transformation&) = delete; |
|||
public: |
|||
typedef strings::default_char_transform input_type; |
|||
typedef strings::default_char_transform output_type; |
|||
}; |
|||
|
|||
|
|||
// Friend classes for digest_data class
|
|||
enum struct comparison_version; |
|||
namespace internal |
|||
{ |
|||
template <comparison_version> class digest_comparison_base; |
|||
class digest_copy; |
|||
} |
|||
template <comparison_version> class digest_comparison; |
|||
template <bool> class digest_position_array_base; |
|||
|
|||
|
|||
// Data structure for fuzzy digest (as base class)
|
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
class digest_data |
|||
{ |
|||
static_assert(digest_params::max_blockhash_len >= 4, |
|||
"max_blockhash_len must be at least 4 due to restrictions in this implementation."); |
|||
|
|||
// Maximum lengths for each block hashes
|
|||
public: |
|||
static constexpr const blockhash_len_t max_blockhash1_len = digest_params::max_blockhash_len; |
|||
static constexpr const blockhash_len_t max_blockhash2_len = IsShort |
|||
? digest_params::max_blockhash_len / 2 |
|||
: digest_params::max_blockhash_len; |
|||
|
|||
// Maximum characters required to pretty-print "natural" digests
|
|||
public: |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<blockhash_len_t, digest_params::max_blockhash_len>, |
|||
safe_int::uvalue<blockhash_len_t, 2> |
|||
>::is_valid, |
|||
"max_blockhash_len * 2 must be in range of blockhash_len_t."); |
|||
static_assert( |
|||
safe_int::safe_add< |
|||
safe_int::safe_add< |
|||
safe_int::uvalue<size_t, 3>, |
|||
safe_int::uvalue<size_t, digest_blocksize::max_natural_digits> |
|||
>, |
|||
safe_int::safe_mul< |
|||
safe_int::uvalue<size_t, digest_params::max_blockhash_len>, |
|||
safe_int::uvalue<size_t, 2> |
|||
> |
|||
>::is_valid, |
|||
"max_blockhash_len * 2 + max_natural_digits + 3 must be in range of size_t."); |
|||
static constexpr const size_t max_natural_chars = |
|||
max_blockhash1_len + max_blockhash2_len + // two block hashes
|
|||
digest_blocksize::max_natural_digits + // block size
|
|||
3; // two colons and '\0' as a terminator
|
|||
// width == chars exclusing '\0' (== chars - 1)
|
|||
static constexpr const size_t max_natural_width = max_natural_chars - 1; |
|||
static constexpr const size_t max_natural_width_digits = |
|||
numeric_digits::in_decimal<size_t>(max_natural_width); |
|||
static_assert(max_natural_width != 0, "sanity check for max_natural_width failed."); |
|||
|
|||
// Data structure
|
|||
private: |
|||
char digest[max_blockhash1_len + max_blockhash2_len]; |
|||
blockhash_len_t blkhash1_len; |
|||
blockhash_len_t blkhash2_len; |
|||
digest_blocksize_t blksize; |
|||
public: |
|||
size_t blockhash1_len(void) const noexcept { return blkhash1_len; } |
|||
size_t blockhash2_len(void) const noexcept { return blkhash2_len; } |
|||
unsigned long blocksize(void) const noexcept { return blksize; } |
|||
size_t digest_size_used(void) const noexcept { return blkhash1_len + blkhash2_len; } |
|||
const char* digest_buffer(void) const noexcept { return digest; } |
|||
void copy_digest_buffer(char* buf) const noexcept { memcpy(buf, digest, digest_size_used()); } |
|||
|
|||
// "Initialization" and assignment
|
|||
public: |
|||
digest_data(void) noexcept = default; // initialize to undefined state
|
|||
digest_data(const digest_data& other) noexcept |
|||
: blkhash1_len(other.blkhash1_len) |
|||
, blkhash2_len(other.blkhash2_len) |
|||
, blksize(other.blksize) |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
if (digest != other.digest) |
|||
memcpy(digest, other.digest, blkhash1_len + blkhash2_len); |
|||
} |
|||
const digest_data& operator=(const digest_data& other) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
blkhash1_len = other.blkhash1_len; |
|||
blkhash2_len = other.blkhash2_len; |
|||
blksize = other.blksize; |
|||
if (digest != other.digest) |
|||
memcpy(digest, other.digest, blkhash1_len + blkhash2_len); |
|||
return *this; |
|||
} |
|||
static void swap(digest_data& a, digest_data& b) noexcept |
|||
{ |
|||
std::swap(a.blksize, b.blksize); |
|||
std::swap(a.blkhash1_len, b.blkhash1_len); |
|||
std::swap(a.blkhash2_len, b.blkhash2_len); |
|||
std::swap(a.digest, b.digest); // C++11 version of swap
|
|||
} |
|||
|
|||
// Validators (for its validness and naturality)
|
|||
public: |
|||
bool is_valid(void) const noexcept |
|||
{ |
|||
if (blkhash1_len > max_blockhash1_len) |
|||
return false; |
|||
if (blkhash2_len > max_blockhash2_len) |
|||
return false; |
|||
if (blksize > 0xfffffffful) |
|||
return false; |
|||
if (IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (digest[i] < char(0) || 64 <= digest[i]) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
bool is_natural(void) const noexcept |
|||
{ |
|||
if (!is_valid()) |
|||
return false; |
|||
if (blkhash1_len < blkhash2_len) |
|||
return false; |
|||
if (!digest_blocksize::is_natural(blksize)) |
|||
return false; |
|||
if (!digest_blocksize::is_safe_to_double(blksize) && blkhash2_len >= 2) |
|||
return false; |
|||
if (!IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (!base64::isbase64(digest[i])) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
bool is_blocksize_natural(void) const noexcept |
|||
{ |
|||
return digest_blocksize::is_natural(blksize); |
|||
} |
|||
bool is_normalized(void) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
return |
|||
!strings::sequences<digest_params::max_blockhash_sequence> |
|||
::has_sequences(digest, blkhash1_len) && |
|||
!strings::sequences<digest_params::max_blockhash_sequence> |
|||
::has_sequences(digest+blkhash1_len, blkhash2_len); |
|||
} |
|||
bool has_valid_base64_data(void) const noexcept |
|||
{ |
|||
if (blkhash1_len > max_blockhash1_len) |
|||
return false; |
|||
if (blkhash2_len > max_blockhash2_len) |
|||
return false; |
|||
if (IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (digest[i] < char(0) || 64 <= digest[i]) |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (!base64::isbase64(digest[i])) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Equality
|
|||
private: |
|||
static bool is_eq_except_blocksize(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
return |
|||
a.blkhash1_len == b.blkhash1_len && |
|||
a.blkhash2_len == b.blkhash2_len && |
|||
memcmp(a.digest, b.digest, a.digest_size_used()) == 0; |
|||
} |
|||
static bool is_eq(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
return |
|||
digest_blocksize::is_near_eq(a.blksize, b.blksize) && |
|||
is_eq_except_blocksize(a, b); |
|||
} |
|||
public: |
|||
friend bool operator==(const digest_data& a, const digest_data& b) noexcept { return is_eq(a, b); } |
|||
friend bool operator!=(const digest_data& a, const digest_data& b) noexcept { return !is_eq(a, b); } |
|||
|
|||
// Default comparison for sorting (in "dictionary" order or whatever)
|
|||
// Note that sort order differs depending on IsAlphabetRestricted.
|
|||
public: |
|||
friend bool operator<(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blksize < b.blksize) |
|||
return true; |
|||
if (a.blksize > b.blksize) |
|||
return false; |
|||
if (a.blkhash1_len < b.blkhash1_len) |
|||
return true; |
|||
if (a.blkhash1_len > b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len < b.blkhash2_len) |
|||
return true; |
|||
if (a.blkhash2_len > b.blkhash2_len) |
|||
return false; |
|||
if (memcmp(a.digest, b.digest, a.digest_size_used()) < 0) |
|||
return true; |
|||
return false; |
|||
} |
|||
friend bool operator>(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blksize > b.blksize) |
|||
return true; |
|||
if (a.blksize < b.blksize) |
|||
return false; |
|||
if (a.blkhash1_len > b.blkhash1_len) |
|||
return true; |
|||
if (a.blkhash1_len < b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len > b.blkhash2_len) |
|||
return true; |
|||
if (a.blkhash2_len < b.blkhash2_len) |
|||
return false; |
|||
if (memcmp(a.digest, b.digest, a.digest_size_used()) > 0) |
|||
return true; |
|||
return false; |
|||
} |
|||
friend bool operator<=(const digest_data& a, const digest_data& b) noexcept { return !(a > b); } |
|||
friend bool operator>=(const digest_data& a, const digest_data& b) noexcept { return !(a < b); } |
|||
|
|||
// Predicates (including non-standard comparison predicates for fast sorting)
|
|||
public: |
|||
struct pred_equal_to |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a == b; } |
|||
}; |
|||
struct pred_not_equal_to |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a != b; } |
|||
}; |
|||
struct pred_less |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a < b; } |
|||
}; |
|||
struct pred_less_equal |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a <= b; } |
|||
}; |
|||
struct pred_greater |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a > b; } |
|||
}; |
|||
struct pred_greater_equal |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a >= b; } |
|||
}; |
|||
struct pred_less_blocksize |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept |
|||
{ |
|||
return a.blksize < b.blksize; |
|||
} |
|||
}; |
|||
struct pred_less_blocksize_natural |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept |
|||
{ |
|||
bool aN = digest_blocksize::is_natural(a.blksize); |
|||
bool bN = digest_blocksize::is_natural(b.blksize); |
|||
if (aN && !bN) |
|||
return false; |
|||
if (!aN && bN) |
|||
return true; |
|||
if (a.blksize < b.blksize) |
|||
return true; |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
// Hash (for sets and dictionaries)
|
|||
public: |
|||
size_t hash(void) const noexcept |
|||
{ |
|||
typedef typename std::conditional< |
|||
(std::numeric_limits<size_t>::max() >= 0xfffffffful), |
|||
size_t, uint_least32_t |
|||
>::type hash_t; |
|||
static constexpr const hash_t fnv_init = 2166136261ul; |
|||
static constexpr const hash_t fnv_prime = 16777619ul; |
|||
hash_t h = fnv_init; |
|||
h ^= hash_t(blksize); h *= fnv_prime; |
|||
h ^= hash_t(blkhash1_len); h *= fnv_prime; |
|||
h ^= hash_t(blkhash2_len); h *= fnv_prime; |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
{ |
|||
h ^= hash_t(static_cast<unsigned char>(digest[i])); |
|||
h *= fnv_prime; |
|||
} |
|||
if (std::numeric_limits<size_t>::max() < 0xfffffffful) |
|||
h ^= (h >> 16); |
|||
return size_t(h); |
|||
} |
|||
|
|||
// Normalization
|
|||
public: |
|||
static void normalize(digest_data& dest, const digest_data& source) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(source.is_valid()); |
|||
#endif |
|||
dest.blksize = source.blksize; |
|||
blockhash_len_t orig_blkhash1_len = source.blkhash1_len; |
|||
dest.blkhash1_len = strings::sequences<digest_params::max_blockhash_sequence> |
|||
::copy_elim_sequences(dest.digest, source.digest, source.blkhash1_len); |
|||
dest.blkhash2_len = strings::sequences<digest_params::max_blockhash_sequence> |
|||
::copy_elim_sequences(dest.digest + dest.blkhash1_len, source.digest + orig_blkhash1_len, source.blkhash2_len); |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(dest.is_valid()); |
|||
assert(dest.is_normalized()); |
|||
#endif |
|||
} |
|||
template <typename Treturn = digest_data> |
|||
static Treturn normalize(const digest_data& source) noexcept |
|||
{ |
|||
Treturn dest; |
|||
digest_data::normalize(dest, source); |
|||
return dest; |
|||
} |
|||
void normalize(void) noexcept |
|||
{ |
|||
normalize(*this, *this); |
|||
} |
|||
|
|||
// Utility to parse digests
|
|||
private: |
|||
template <template <char... terms> class Tstring_copy> |
|||
static bool parse_internal(digest_data& digest, const char* str) noexcept |
|||
{ |
|||
const char* rem = str; |
|||
char* out = digest.digest; |
|||
errno = 0; |
|||
// size of unsigned long is at least 32-bit
|
|||
unsigned long blksize = strtoul(str, const_cast<char**>(&rem), 10); |
|||
if (rem == str) |
|||
return false; |
|||
if (errno == ERANGE && blksize == std::numeric_limits<unsigned long>::max()) |
|||
return false; |
|||
if (blksize > 0xfffffffful) |
|||
return false; |
|||
digest.blksize = digest_blocksize_t(blksize); |
|||
if (*rem++ != ':') |
|||
return false; |
|||
if (!Tstring_copy<':'>::copy_elim_sequences(out, max_blockhash1_len, rem)) |
|||
return false; |
|||
digest.blkhash1_len = out - digest.digest; |
|||
char* outtmp = out; |
|||
if (*rem++ != ':') |
|||
return false; |
|||
#ifndef FFUZZYPP_BLOCKDIGEST2_TERMS |
|||
// default from ffuzzy++ 4.0
|
|||
#define FFUZZYPP_BLOCKDIGEST2_TERMS ',' |
|||
#endif |
|||
if (!Tstring_copy<FFUZZYPP_BLOCKDIGEST2_TERMS>:: |
|||
copy_elim_sequences(out, max_blockhash2_len, rem)) |
|||
return false; |
|||
digest.blkhash2_len = out - outtmp; |
|||
if (IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0; i < digest.blkhash1_len + digest.blkhash2_len; i++) |
|||
{ |
|||
if (digest.digest[i] == base64::invalid_index) |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
protected: |
|||
static bool parse(digest_data& digest, const char* str) noexcept |
|||
{ |
|||
return parse_internal< |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::input_type>::template string_copy |
|||
>(digest, str); |
|||
} |
|||
static bool parse(digest_data& digest, const std::string& str) |
|||
{ |
|||
return parse(digest, str.c_str()); |
|||
} |
|||
static bool parse_normalized(digest_data& digest, const char* str) noexcept |
|||
{ |
|||
return parse_internal< |
|||
strings::sequences<digest_params::max_blockhash_sequence, |
|||
typename digest_data_transformation<IsAlphabetRestricted>::input_type>::template string_copy |
|||
>(digest, str); |
|||
} |
|||
static bool parse_normalized(digest_data& digest, const std::string& str) |
|||
{ |
|||
return parse_normalized(digest, str.c_str()); |
|||
} |
|||
|
|||
// Pretty printing
|
|||
public: |
|||
bool pretty_unsafe(char* out) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
int n = sprintf(out, "%lu:", static_cast<unsigned long>(blksize)); |
|||
if (n < 0) |
|||
return false; |
|||
out += n; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest, blkhash1_len); |
|||
out += blkhash1_len; |
|||
*out++ = ':'; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest+blkhash1_len, blkhash2_len); |
|||
out[blkhash2_len] = '\0'; |
|||
return true; |
|||
} |
|||
bool pretty(char* out, size_t outsize) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
int n = snprintf(out, outsize, "%lu:", static_cast<unsigned long>(blksize)); |
|||
if (n < 0) |
|||
return false; |
|||
if (size_t(n) == outsize) |
|||
return false; |
|||
outsize -= size_t(n); |
|||
out += n; |
|||
if (outsize < size_t(blkhash1_len) + size_t(blkhash2_len) + 2) |
|||
return false; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest, blkhash1_len); |
|||
out += blkhash1_len; |
|||
*out++ = ':'; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest+blkhash1_len, blkhash2_len); |
|||
out[blkhash2_len] = '\0'; |
|||
return true; |
|||
} |
|||
std::string pretty(void) const |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
unsigned long bs(blksize); |
|||
std::string str(std::to_string(bs)); |
|||
str.push_back(':'); |
|||
for (blockhash_len_t i = 0; i < blkhash1_len; i++) |
|||
str.push_back(digest_data_transformation<IsAlphabetRestricted>::output_type::transform(digest[i])); |
|||
str.push_back(':'); |
|||
for (blockhash_len_t i = 0; i < blkhash2_len; i++) |
|||
str.push_back(digest_data_transformation<IsAlphabetRestricted>::output_type::transform(digest[blkhash1_len + i])); |
|||
return str; |
|||
} |
|||
|
|||
// Friend classes
|
|||
template <comparison_version> friend class internal::digest_comparison_base; |
|||
template <comparison_version> friend class digest_comparison; |
|||
template <bool> friend class digest_position_array_base; |
|||
friend class digest_generator; |
|||
friend class internal::digest_copy; |
|||
}; |
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
// Utility to copy constrained digest data to non-constrained digest object
|
|||
class digest_copy |
|||
{ |
|||
private: |
|||
digest_copy(void) = delete; |
|||
digest_copy(const digest_copy&) = delete; |
|||
public: |
|||
template <bool IsAlphabetRestricted> |
|||
static void copy_to_long( |
|||
digest_data<IsAlphabetRestricted, false>& dest, |
|||
const digest_data<IsAlphabetRestricted, true>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
memcpy(dest.digest, src.digest, src.blkhash1_len + src.blkhash2_len); |
|||
} |
|||
template <bool IsShort> |
|||
static void copy_to_non_ra( |
|||
digest_data<false, IsShort>& dest, |
|||
const digest_data<true, IsShort>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
strings::nosequences<base64::transform_to_b64>:: |
|||
copy_raw(dest.digest, src.digest, src.blkhash1_len + src.blkhash2_len); |
|||
} |
|||
static void copy_to_long_non_ra( |
|||
digest_data<false, false>& dest, |
|||
const digest_data<true, true>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
strings::nosequences<base64::transform_to_b64>:: |
|||
copy_raw(dest.digest, src.digest, src.blkhash1_len + src.blkhash2_len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,125 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_filesize.hpp |
|||
File size utilities |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_FILESIZE_HPP |
|||
#define FFUZZYPP_DIGEST_FILESIZE_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "utils/safe_int.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
typedef uint_least64_t digest_filesize_t; |
|||
|
|||
// File size utilities
|
|||
class digest_filesize |
|||
{ |
|||
static_assert(safe_int::contracts::is_unsigned_integral_type<digest_filesize_t>(), |
|||
"digest_filesize_t must be an unsigned integral type."); |
|||
private: |
|||
digest_filesize(void) = delete; |
|||
digest_filesize(const digest_filesize&) = delete; |
|||
|
|||
// Minimum size (as supported by ssdeep)
|
|||
private: |
|||
static constexpr const unsigned long u_min_supported_size = 4096 + 1; |
|||
public: |
|||
static_assert(u_min_supported_size <= 0xfffffffful, |
|||
"u_min_supported_size must be in range of 32-bit unsigned integer."); |
|||
static_assert(safe_int::uvalue<digest_filesize_t, u_min_supported_size>::is_valid, |
|||
"u_min_supported_size must be in range of digest_filesize_t."); |
|||
static constexpr const digest_filesize_t min_supported_size = u_min_supported_size; |
|||
|
|||
// Maximum size (theoretical limit)
|
|||
public: |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::safe_mul< |
|||
safe_int::uvalue<digest_filesize_t, digest_blocksize::max_blocksize>, |
|||
safe_int::uvalue<digest_filesize_t, digest_params::max_blockhash_len> |
|||
>, |
|||
safe_int::uvalue<digest_filesize_t, 1> |
|||
>::is_valid, |
|||
"max_blocksize * max_blockhash_len + 1 (== max_size + 1) must be " |
|||
"in range of digest_filesize_t."); |
|||
static constexpr const digest_filesize_t max_size = |
|||
digest_filesize_t(digest_blocksize::max_blocksize) * |
|||
digest_filesize_t(digest_params::max_blockhash_len); |
|||
static constexpr const digest_filesize_t max_theoretical_size = max_size; |
|||
|
|||
// Maximum portable size (with ssdeep 2.6-2.12)
|
|||
public: |
|||
static_assert(safe_int::uvalue<digest_filesize_t, 0xfffffffful>::is_valid, |
|||
"digest_filesize_t must be able to represent all 32-bit unsigned integer values."); |
|||
static constexpr const digest_filesize_t max_portable_size = |
|||
minmax::min(max_size, digest_filesize_t(0xfffffffful)); |
|||
static_assert(min_supported_size <= max_portable_size, |
|||
"min_supported_size must not exceed max_portable_size."); |
|||
|
|||
// Predicates
|
|||
public: |
|||
static constexpr bool is_portable(digest_filesize_t total_size) noexcept |
|||
{ |
|||
/*
|
|||
WARNING: Given size may not be "portable" on insane architectures. |
|||
|
|||
Portability: |
|||
* ffuzzy++ (3.0) |
|||
* ssdeep (including 2.6-2.12) |
|||
|
|||
Note: |
|||
Mr.Kornblum (the original author of ssdeep) considers |
|||
version 2.9 the "standard". Note that this release does not |
|||
support files equal to or larger than 4GiB. |
|||
*/ |
|||
return total_size >= min_supported_size && total_size <= max_portable_size; |
|||
} |
|||
static constexpr bool is_supported_by_ssdeep_2_12(digest_filesize_t total_size) noexcept |
|||
{ |
|||
return total_size >= min_supported_size && total_size <= max_portable_size; |
|||
} |
|||
static constexpr bool is_supported_by_ffuzzy_3_0(digest_filesize_t total_size) noexcept |
|||
{ |
|||
return total_size <= max_theoretical_size; |
|||
} |
|||
static constexpr bool is_supported(digest_filesize_t total_size) noexcept |
|||
{ |
|||
// whether supported by "this" version of ffuzzy++
|
|||
return is_supported_by_ffuzzy_3_0(total_size); |
|||
} |
|||
|
|||
// Predicate (equivalent to "not meaningful results" in ssdeep)
|
|||
public: |
|||
static constexpr bool is_not_meaningful(digest_filesize_t total_size) noexcept |
|||
{ |
|||
return total_size < min_supported_size; |
|||
} |
|||
}; |
|||
|
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,544 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_generator.hpp |
|||
Fuzzy digest generator |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_GENERATOR_HPP |
|||
#define FFUZZYPP_DIGEST_GENERATOR_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstdint> |
|||
#include <cstddef> |
|||
|
|||
#include <algorithm> |
|||
#include <string> |
|||
|
|||
#include "base64.hpp" |
|||
#include "context_hash.hpp" |
|||
#include "context_hash_fast.hpp" |
|||
#include "rolling_hash.hpp" |
|||
#include "rolling_hash_ssdeep.hpp" |
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "digest_base.hpp" |
|||
#include "digest_filesize.hpp" |
|||
#include "utils/likely.hpp" |
|||
#include "utils/safe_int.hpp" |
|||
#include "strings/transform.hpp" |
|||
#include "strings/sequences.hpp" |
|||
#include "strings/nosequences.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
struct digest_generator_error {}; |
|||
|
|||
class digest_generator |
|||
{ |
|||
// Digest characters and transformation for them
|
|||
private: |
|||
/*
|
|||
This type contains digest characters as Base64 indices. |
|||
To distinguish valid and invalid characters, |
|||
we need a "nil" value. |
|||
|
|||
We transform Base64 indices to actual Base64 characters |
|||
when we are copying final digest value. |
|||
*/ |
|||
typedef base64::transform_to_b64 digest_transform_t; |
|||
static constexpr const char digest_nil = 64; |
|||
static_assert(digest_nil < 0 || digest_nil > 63, |
|||
"digest_nil must be out of Base64 range."); |
|||
|
|||
// Heuristics to guess maximum file size from block size (or its index)
|
|||
public: |
|||
static constexpr digest_filesize_t guessed_filesize(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return digest_filesize_t(blocksize) * digest_params::max_blockhash_len; |
|||
} |
|||
static constexpr digest_filesize_t guessed_filesize_at(unsigned i) noexcept |
|||
{ |
|||
return guessed_filesize(digest_blocksize::at(i)); |
|||
} |
|||
|
|||
// Heuristic to guess block size from file size
|
|||
public: |
|||
static unsigned blockhash_index_guessed_by_filesize(digest_filesize_t size, unsigned start = 0) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(start < digest_blocksize::number_of_blockhashes); |
|||
assert(size <= digest_filesize::max_size); |
|||
#endif |
|||
unsigned bi = start; |
|||
digest_filesize_t bgs = guessed_filesize_at(bi); |
|||
while (bgs < size) |
|||
{ |
|||
bi++; |
|||
bgs *= 2; |
|||
} |
|||
return bi; |
|||
} |
|||
|
|||
// Data Structure
|
|||
private: |
|||
typedef context_hash_fast context_hash_t; |
|||
struct blockhash_context |
|||
{ |
|||
context_hash_t hfull; |
|||
context_hash_t hhalf; |
|||
char digest[digest_params::max_blockhash_len]; |
|||
char digesth; |
|||
blockhash_len_t dindex; |
|||
}; |
|||
blockhash_context bh[digest_blocksize::number_of_blockhashes]; |
|||
digest_filesize_t totalsz; |
|||
digest_filesize_t totalsz_constant; |
|||
digest_filesize_t reduce_border; |
|||
rolling_hash roll; |
|||
unsigned bhstart; |
|||
unsigned bhend; |
|||
unsigned bhendlimit; |
|||
uint_least32_t rollmask; |
|||
context_hash_t hlast; |
|||
unsigned flags; |
|||
static constexpr const unsigned FLAG_LASTHASH = 0x1; |
|||
static constexpr const unsigned FLAG_SZCONSTANT = 0x2; |
|||
|
|||
// Simple data structure manipulation
|
|||
public: |
|||
bool is_total_size_clamped(void) const noexcept { return totalsz > digest_filesize::max_size; } |
|||
bool is_file_size_constant(void) const noexcept { return flags & FLAG_SZCONSTANT; } |
|||
unsigned blockhash_index_start(void) const noexcept { return bhstart; } |
|||
unsigned blockhash_index_end(void) const noexcept { return bhend; } |
|||
unsigned blockhash_index_end_limit(void) const noexcept { return bhendlimit; } |
|||
digest_filesize_t total_size(void) const noexcept { return totalsz; } |
|||
// constant file size (for fast ssdeep hashing)
|
|||
digest_filesize_t get_file_size_constant(void) const noexcept { return totalsz_constant; } |
|||
bool set_file_size_constant(digest_filesize_t size) noexcept |
|||
{ |
|||
if (is_file_size_constant() && totalsz_constant != size) |
|||
return false; |
|||
if (size > digest_filesize::max_size) |
|||
return false; |
|||
totalsz_constant = size; |
|||
bhendlimit = std::min( |
|||
digest_blocksize::number_of_blockhashes - 1, |
|||
blockhash_index_guessed_by_filesize(size) + 1 |
|||
); |
|||
flags |= FLAG_SZCONSTANT; |
|||
return true; |
|||
} |
|||
|
|||
public: |
|||
// Reset minimum context required
|
|||
void reset(void) noexcept |
|||
{ |
|||
bh[0].hfull.reset(); |
|||
bh[0].hhalf.reset(); |
|||
bh[0].digest[digest_params::max_blockhash_len - 1] = digest_nil; |
|||
bh[0].digesth = digest_nil; |
|||
bh[0].dindex = 0; |
|||
totalsz = 0; |
|||
reduce_border = guessed_filesize_at(0); |
|||
roll.reset(); |
|||
bhstart = 0; |
|||
bhend = 1; |
|||
bhendlimit = digest_blocksize::number_of_blockhashes - 1; |
|||
rollmask = 0; |
|||
flags = 0; |
|||
} |
|||
|
|||
// Update functions (by buffer or by character)
|
|||
public: |
|||
void update(const unsigned char* buf, size_t len) noexcept |
|||
{ |
|||
rolling_hash r = roll; |
|||
if (FFUZZYPP_UNLIKELY(len > digest_filesize::max_size |
|||
|| digest_filesize::max_size - digest_filesize_t(len) < totalsz)) |
|||
{ |
|||
totalsz = digest_filesize::max_size + 1; |
|||
} |
|||
else |
|||
{ |
|||
totalsz += digest_filesize_t(len); |
|||
} |
|||
while (len--) |
|||
{ |
|||
unsigned char c = *buf++; |
|||
r.update(c); |
|||
for (unsigned i = bhstart; i < bhend; i++) |
|||
{ |
|||
bh[i].hfull.update(c); |
|||
bh[i].hhalf.update(c); |
|||
} |
|||
if (flags & FLAG_LASTHASH) |
|||
hlast.update(c); |
|||
uint_least32_t horg = (r.sum() + 1) & uint_least32_t(0xfffffffful); |
|||
uint_least32_t h = horg / uint_least32_t(digest_blocksize::min_blocksize); |
|||
if (0xfffffffful % digest_blocksize::min_blocksize != digest_blocksize::min_blocksize - 1 && !horg) |
|||
continue; |
|||
if (FFUZZYPP_LIKELY(h & rollmask)) |
|||
continue; |
|||
if (horg % uint_least32_t(digest_blocksize::min_blocksize)) |
|||
continue; |
|||
h >>= bhstart; |
|||
unsigned i = bhstart; |
|||
do |
|||
{ |
|||
if (FFUZZYPP_UNLIKELY(bh[i].dindex == 0)) |
|||
{ |
|||
// fork to prepare larger block sizes
|
|||
if (bhend > bhendlimit) |
|||
{ |
|||
if (bhendlimit == digest_blocksize::number_of_blockhashes - 1 |
|||
&& !(flags & FLAG_LASTHASH)) |
|||
{ |
|||
hlast = bh[i].hfull; |
|||
flags |= FLAG_LASTHASH; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
bh[i+1].hfull = bh[i].hfull; |
|||
bh[i+1].hhalf = bh[i].hhalf; |
|||
bh[i+1].digest[digest_params::max_blockhash_len - 1] = digest_nil; |
|||
bh[i+1].digesth = digest_nil; |
|||
bh[i+1].dindex = 0; |
|||
bhend++; |
|||
} |
|||
} |
|||
bh[i].digest[bh[i].dindex] = bh[i].hfull.sum_in_base64(); |
|||
bh[i].digesth = bh[i].hhalf.sum_in_base64(); |
|||
if (bh[i].dindex < digest_params::max_blockhash_len - 1) |
|||
{ |
|||
bh[i].dindex++; |
|||
bh[i].hfull.reset(); |
|||
if (bh[i].dindex < digest_params::max_blockhash_len / 2) |
|||
{ |
|||
bh[i].digesth = digest_nil; |
|||
bh[i].hhalf.reset(); |
|||
} |
|||
} |
|||
// eliminate block sizes which will not be chosen
|
|||
else if (FFUZZYPP_UNLIKELY(bhend - bhstart >= 2 |
|||
&& reduce_border < (is_file_size_constant() ? totalsz_constant : totalsz) |
|||
&& bh[i+1].dindex >= digest_params::max_blockhash_len / 2)) |
|||
{ |
|||
bhstart++; |
|||
rollmask = rollmask * 2 + 1; |
|||
reduce_border *= 2; |
|||
} |
|||
if (h & 1) |
|||
break; |
|||
h >>= 1; |
|||
} while (++i < bhend); |
|||
} |
|||
roll = r; |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
unsigned char C(c); |
|||
update(&C, 1); |
|||
} |
|||
|
|||
// High-level update utilities
|
|||
// (by file pointer or by file name; w/ or w/o internal buffer)
|
|||
public: |
|||
static constexpr const size_t default_buffer_size = 4096; |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_stream(FILE* fp, unsigned char* tmpbuf) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
if (!fp) |
|||
return false; |
|||
while (true) |
|||
{ |
|||
size_t n = fread(tmpbuf, 1, buffer_size, fp); |
|||
if (n == 0) |
|||
break; |
|||
update(tmpbuf, n); |
|||
} |
|||
if (feof(fp)) |
|||
return true; |
|||
return false; |
|||
} |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_stream(FILE* fp) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
unsigned char buf[buffer_size]; |
|||
return update_by_stream<buffer_size>(fp, buf); |
|||
} |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_file(const char* filename) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
FILE* fp = fopen(filename, "rb"); |
|||
if (!fp) |
|||
return false; |
|||
bool ret = update_by_stream<buffer_size>(fp); |
|||
fclose(fp); |
|||
return ret; |
|||
} |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_file(const char* filename, unsigned char* tmpbuf) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
FILE* fp = fopen(filename, "rb"); |
|||
if (!fp) |
|||
return false; |
|||
bool ret = update_by_stream<buffer_size>(fp, tmpbuf); |
|||
fclose(fp); |
|||
return ret; |
|||
} |
|||
|
|||
// Digest finalization
|
|||
private: |
|||
// Heuristic to guess block hash index to start from the current state
|
|||
unsigned blockhash_index_guessed_to_start(void) const noexcept |
|||
{ |
|||
unsigned bi = blockhash_index_guessed_by_filesize(totalsz, bhstart); |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(bi < digest_blocksize::number_of_blockhashes); |
|||
#endif |
|||
bi = std::min(bi, bhend - 1); |
|||
while (bi > bhstart && bh[bi].dindex < digest_params::max_blockhash_len / 2) |
|||
bi--; |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(bi >= bhstart && bi < bhend); |
|||
assert(bi == 0 || bh[bi].dindex >= digest_params::max_blockhash_len / 2); |
|||
#endif |
|||
return bi; |
|||
} |
|||
// Copy the final (resulting) digest
|
|||
template <typename Tseq, bool IsAlphabetRestricted, bool IsShort, bool Shortened> |
|||
bool copy_digest_internal(digest_data<IsAlphabetRestricted, IsShort>& digest) noexcept |
|||
{ |
|||
/*
|
|||
This function is not exactly "const" but mostly constant. |
|||
You can call this function multiple times as you need. |
|||
*/ |
|||
static_assert(Shortened == true || IsShort == false, |
|||
"copying long result to short digest_data structure is prohibited."); |
|||
if (is_total_size_clamped()) |
|||
return false; |
|||
if (is_file_size_constant() && totalsz != totalsz_constant) |
|||
return false; |
|||
unsigned bi = blockhash_index_guessed_to_start(); |
|||
digest.blksize = digest_blocksize::at(bi); |
|||
|
|||
uint_least32_t rh = roll.sum(); |
|||
// Copy first block hash (digest)
|
|||
{ |
|||
char chtmp = bh[bi].digest[digest_params::max_blockhash_len - 1]; |
|||
size_t sz = bh[bi].dindex; |
|||
if (rh != 0) |
|||
bh[bi].digest[sz++] = bh[bi].hfull.sum_in_base64(); |
|||
else if (chtmp != digest_nil) |
|||
sz++; |
|||
digest.blkhash1_len = Tseq::copy_elim_sequences(digest.digest, bh[bi].digest, sz); |
|||
bh[bi].digest[digest_params::max_blockhash_len - 1] = chtmp; |
|||
} |
|||
// Copy second block hash if we need
|
|||
if (bi < bhend - 1) |
|||
{ |
|||
size_t dindex = bh[bi+1].dindex; |
|||
if (Shortened) |
|||
dindex = std::min(dindex, size_t(digest_params::max_blockhash_len / 2 - 1)); |
|||
char chtmp = bh[bi+1].digest[dindex]; |
|||
size_t sz = dindex; |
|||
if (rh != 0) |
|||
{ |
|||
bh[bi+1].digest[sz++] = Shortened |
|||
? bh[bi+1].hhalf.sum_in_base64() |
|||
: bh[bi+1].hfull.sum_in_base64(); |
|||
} |
|||
else |
|||
{ |
|||
if (Shortened) |
|||
{ |
|||
if (bh[bi+1].digesth != digest_nil) |
|||
bh[bi+1].digest[sz++] = bh[bi+1].digesth; |
|||
} |
|||
else |
|||
{ |
|||
if (dindex == digest_params::max_blockhash_len - 1 && chtmp != digest_nil) |
|||
sz++; |
|||
} |
|||
} |
|||
digest.blkhash2_len = Tseq::copy_elim_sequences( |
|||
digest.digest + digest.blkhash1_len, bh[bi+1].digest, sz); |
|||
bh[bi+1].digest[dindex] = chtmp; |
|||
} |
|||
else if (rh != 0) |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(bi == 0 || bi == digest_blocksize::number_of_blockhashes - 1); |
|||
#endif |
|||
if (bi == 0) |
|||
digest.digest[digest.blkhash1_len] = bh[bi].hfull.sum_in_base64(); |
|||
else |
|||
digest.digest[digest.blkhash1_len] = hlast.sum_in_base64(); |
|||
digest.blkhash2_len = Tseq::copy_elim_sequences( |
|||
digest.digest + digest.blkhash1_len, |
|||
digest.digest + digest.blkhash1_len, |
|||
1); |
|||
} |
|||
else |
|||
{ |
|||
digest.blkhash2_len = 0; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Digest finalization (and copying) utilities
|
|||
private: |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool Shortened> |
|||
bool copy_digest_base(digest_data<IsAlphabetRestricted, IsShort>& digest) noexcept |
|||
{ |
|||
return copy_digest_internal< |
|||
strings::nosequences< |
|||
typename digest_data_transformation<!IsAlphabetRestricted>::output_type |
|||
>, |
|||
IsAlphabetRestricted, IsShort, Shortened>(digest); |
|||
} |
|||
public: |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool Shortened = true> |
|||
bool copy_digest_normalized(digest_data<IsAlphabetRestricted, IsShort>& digest) noexcept |
|||
{ |
|||
return copy_digest_internal< |
|||
strings::sequences< |
|||
digest_params::max_blockhash_sequence, |
|||
typename digest_data_transformation<!IsAlphabetRestricted>::output_type |
|||
>, IsAlphabetRestricted, IsShort, Shortened |
|||
>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
bool copy_digest(digest_base<IsAlphabetRestricted, IsShort, true>& digest) noexcept |
|||
{ |
|||
return copy_digest_normalized<IsAlphabetRestricted, IsShort, true>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
bool copy_digest(digest_base<IsAlphabetRestricted, IsShort, false>& digest) noexcept |
|||
{ |
|||
return copy_digest_base<IsAlphabetRestricted, IsShort, true>(digest); |
|||
} |
|||
public: |
|||
template <bool IsAlphabetRestricted> |
|||
bool copy_digest_long_normalized(digest_base<IsAlphabetRestricted, false, true>& digest) noexcept |
|||
{ |
|||
return copy_digest_normalized<IsAlphabetRestricted, false, false>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted> |
|||
bool copy_digest_long(digest_base<IsAlphabetRestricted, false, true>& digest) noexcept |
|||
{ |
|||
return copy_digest_normalized<IsAlphabetRestricted, false, false>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted> |
|||
bool copy_digest_long(digest_base<IsAlphabetRestricted, false, false>& digest) noexcept |
|||
{ |
|||
return copy_digest_base<IsAlphabetRestricted, false, false>(digest); |
|||
} |
|||
private: |
|||
template <typename T, bool IsAlphabetRestricted, bool IsShort, bool IsNormalized, bool Shortened = true> |
|||
T digest_in_type(void) |
|||
{ |
|||
// This object to be erased by NRVO.
|
|||
T d; |
|||
if (IsNormalized) |
|||
{ |
|||
if (!copy_digest_normalized<IsAlphabetRestricted, IsShort, Shortened>(d)) |
|||
throw digest_generator_error(); |
|||
} |
|||
else |
|||
{ |
|||
if (!copy_digest_base<IsAlphabetRestricted, IsShort, Shortened>(d)) |
|||
throw digest_generator_error(); |
|||
} |
|||
return d; |
|||
} |
|||
public: |
|||
digest_unorm_t digest(void) |
|||
{ |
|||
// Default options as you need (unnormalized; short form)
|
|||
return digest_in_type<digest_unorm_t, false, true, false>(); |
|||
} |
|||
std::string digest_str(void) |
|||
{ |
|||
return digest().pretty(); |
|||
} |
|||
public: |
|||
template <bool Shortened = false> |
|||
digest_long_unorm_t digest_long(void) |
|||
{ |
|||
return digest_in_type<digest_long_unorm_t, false, false, false, Shortened>(); |
|||
} |
|||
digest_t digest_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_t, false, true, true>(); |
|||
} |
|||
template <bool Shortened = false> |
|||
digest_long_t digest_long_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_long_t, false, false, true, Shortened>(); |
|||
} |
|||
public: |
|||
digest_ra_unorm_t digest_ra(void) |
|||
{ |
|||
return digest_in_type<digest_ra_unorm_t, true, true, false>(); |
|||
} |
|||
template <bool Shortened = false> |
|||
digest_ra_long_unorm_t digest_ra_long(void) |
|||
{ |
|||
return digest_in_type<digest_ra_long_unorm_t, true, false, false, Shortened>(); |
|||
} |
|||
digest_ra_t digest_ra_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_ra_t, true, true, true>(); |
|||
} |
|||
template <bool Shortened = false> |
|||
digest_ra_long_t digest_ra_long_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_ra_long_t, true, false, true, Shortened>(); |
|||
} |
|||
|
|||
// Constructors
|
|||
public: |
|||
digest_generator(void) noexcept |
|||
{ |
|||
reset(); |
|||
} |
|||
digest_generator(const digest_generator&) noexcept = default; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,425 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_position_array.hpp |
|||
Fuzzy digest (position array representation) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_POSITION_ARRAY_HPP |
|||
#define FFUZZYPP_DIGEST_POSITION_ARRAY_HPP |
|||
|
|||
#include <algorithm> |
|||
#include <string> |
|||
|
|||
#include "digest_base.hpp" |
|||
#include "digest_position_array_base.hpp" |
|||
#include "digest_comparison.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
template <bool IsAlphabetRestricted> |
|||
class digest_position_array |
|||
: public digest_position_array_base<IsAlphabetRestricted> |
|||
{ |
|||
public: |
|||
static constexpr const bool is_alphabet_restricted = IsAlphabetRestricted; |
|||
|
|||
public: |
|||
digest_position_array(void) noexcept = default; // initialize to undefined state
|
|||
digest_position_array(const digest_position_array& other) noexcept |
|||
: digest_position_array_base<IsAlphabetRestricted>(other) {} |
|||
const digest_position_array& operator=(const digest_position_array& other) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::operator=(other); |
|||
return *this; |
|||
} |
|||
|
|||
// Initialization by digest
|
|||
public: |
|||
template <bool IsShort> |
|||
digest_position_array(const digest_base<IsAlphabetRestricted, IsShort, true>& src) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(*this, src); |
|||
} |
|||
template <bool IsShort> |
|||
digest_position_array(const digest_base<IsAlphabetRestricted, IsShort, false>& src) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(*this, digest_data<IsAlphabetRestricted, IsShort>::normalize(src)); |
|||
} |
|||
public: |
|||
template <bool IsShort> |
|||
static void construct( |
|||
digest_position_array& dest, |
|||
const digest_base<IsAlphabetRestricted, IsShort, true>& src |
|||
) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(dest, src); |
|||
} |
|||
template <bool IsShort> |
|||
static void construct( |
|||
digest_position_array& dest, |
|||
const digest_base<IsAlphabetRestricted, IsShort, false>& src |
|||
) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(dest, digest_data<IsAlphabetRestricted, IsShort>::normalize(src)); |
|||
} |
|||
|
|||
// Initialization by digest string
|
|||
public: |
|||
explicit digest_position_array(const char* str) noexcept(false) |
|||
{ |
|||
digest_base<IsAlphabetRestricted, false, true> digest(str); |
|||
construct(*this, digest); |
|||
} |
|||
explicit digest_position_array(const std::string& str) |
|||
: digest_position_array(str.c_str()) {} |
|||
|
|||
// Comparison
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_lt(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_lt(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_gt(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(other, *this); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_gt(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(other, *this); |
|||
} |
|||
|
|||
// Comparison (on different digests)
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(b, a); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_diff(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_diff(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_diff(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_diff(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq_diff(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq_diff(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq_diff<Version>(*this, other); |
|||
} |
|||
}; |
|||
|
|||
typedef digest_position_array< true> digest_position_array_t; |
|||
typedef digest_position_array<false> digest_position_array_non_ra_t; |
|||
|
|||
} |
|||
|
|||
|
|||
// Specialization of standard swap
|
|||
namespace std |
|||
{ |
|||
template <bool IsAlphabetRestricted> |
|||
inline void swap( |
|||
ffuzzy::digest_position_array<IsAlphabetRestricted>& a, |
|||
ffuzzy::digest_position_array<IsAlphabetRestricted>& b |
|||
) noexcept |
|||
{ |
|||
ffuzzy::digest_position_array<IsAlphabetRestricted>::swap(a, b); |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,379 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_position_array_base.hpp |
|||
Fuzzy digest (position array representation; base class) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_POSITION_ARRAY_BASE_HPP |
|||
#define FFUZZYPP_DIGEST_POSITION_ARRAY_BASE_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
|
|||
#include <algorithm> |
|||
#include <limits> |
|||
|
|||
#include "base64.hpp" |
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "strings/position_array.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
template <bool IsAlphabetRestricted> |
|||
class digest_position_array_params |
|||
{ |
|||
private: |
|||
digest_position_array_params(void) = delete; |
|||
digest_position_array_params(const digest_position_array_params&) = delete; |
|||
public: |
|||
static constexpr const char char_min = |
|||
IsAlphabetRestricted ? 0x00 : std::numeric_limits<char>::min(); |
|||
static constexpr const char char_max = |
|||
IsAlphabetRestricted ? 0x3f : std::numeric_limits<char>::max(); |
|||
static constexpr const bool is_available = |
|||
strings::is_auto_position_array_available<digest_params::max_blockhash_len, char, char_min, char_max>::value; |
|||
}; |
|||
|
|||
|
|||
// Friend classes for digest_data class
|
|||
enum struct comparison_version; |
|||
namespace internal |
|||
{ |
|||
template <comparison_version> class digest_comparison_base; |
|||
} |
|||
template <comparison_version> class digest_comparison; |
|||
|
|||
|
|||
template <bool IsAlphabetRestricted> |
|||
class digest_position_array_base |
|||
{ |
|||
public: |
|||
typedef strings::auto_position_array< |
|||
digest_params::max_blockhash_len, char, |
|||
digest_position_array_params<IsAlphabetRestricted>::char_min, |
|||
digest_position_array_params<IsAlphabetRestricted>::char_max |
|||
> traits_type; |
|||
typedef typename traits_type::type pa_type; |
|||
typedef typename traits_type::int_type int_type; |
|||
|
|||
// Data structure
|
|||
private: |
|||
pa_type blkhash1; |
|||
pa_type blkhash2; |
|||
blockhash_len_t blkhash1_len; |
|||
blockhash_len_t blkhash2_len; |
|||
digest_blocksize_t blksize; |
|||
public: |
|||
size_t blockhash1_len(void) const noexcept { return blkhash1_len; } |
|||
size_t blockhash2_len(void) const noexcept { return blkhash2_len; } |
|||
unsigned long blocksize(void) const noexcept { return blksize; } |
|||
const pa_type& blockhash1_array(void) const noexcept { return blkhash1; } |
|||
const pa_type& blockhash2_array(void) const noexcept { return blkhash2; } |
|||
bool is_short(void) const noexcept { return blkhash2_len <= digest_params::max_blockhash_len / 2; } |
|||
|
|||
// "Initialization" and assignment
|
|||
public: |
|||
digest_position_array_base(void) noexcept = default; // initialize to undefined state
|
|||
digest_position_array_base(const digest_position_array_base& other) noexcept |
|||
: blkhash1(other.blkhash1) |
|||
, blkhash2(other.blkhash2) |
|||
, blkhash1_len(other.blkhash1_len) |
|||
, blkhash2_len(other.blkhash2_len) |
|||
, blksize(other.blksize) |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
} |
|||
const digest_position_array_base& operator=(const digest_position_array_base& other) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
blkhash1 = other.blkhash1; |
|||
blkhash2 = other.blkhash2; |
|||
blkhash1_len = other.blkhash1_len; |
|||
blkhash2_len = other.blkhash2_len; |
|||
blksize = other.blksize; |
|||
return *this; |
|||
} |
|||
static void swap(digest_position_array_base& a, digest_position_array_base& b) noexcept |
|||
{ |
|||
std::swap(a.blkhash1, b.blkhash1); |
|||
std::swap(a.blkhash2, b.blkhash2); |
|||
std::swap(a.blkhash1_len, b.blkhash1_len); |
|||
std::swap(a.blkhash2_len, b.blkhash2_len); |
|||
std::swap(a.blksize, b.blksize); |
|||
} |
|||
|
|||
// Construction by digest
|
|||
protected: |
|||
template <bool IsShort> |
|||
static void construct( |
|||
digest_position_array_base& dest, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1.construct(src.digest, src.blkhash1_len); |
|||
dest.blkhash2.construct(src.digest+src.blkhash1_len, src.blkhash2_len); |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
} |
|||
template <bool IsShort> |
|||
digest_position_array_base(const digest_data<IsAlphabetRestricted, IsShort>& src) noexcept |
|||
{ |
|||
construct(src); |
|||
} |
|||
|
|||
// Validators (for its validness and naturality)
|
|||
private: |
|||
bool is_valid_blockhash_position_array(const pa_type& parray, blockhash_len_t len) const noexcept |
|||
{ |
|||
int_type mask, set_bits; |
|||
const int_type* data = parray.bitmap_data(); |
|||
size_t array_size = parray.array_size; |
|||
if (len) |
|||
{ |
|||
mask = int_type(1u) << (len - 1); |
|||
mask = mask ^ (-mask); |
|||
} |
|||
else |
|||
{ |
|||
mask = -1; |
|||
} |
|||
set_bits = 0; |
|||
for (size_t i = 0; i < array_size; i++) |
|||
{ |
|||
int_type D = data[i]; |
|||
// check for invalid bits "above" length of the string
|
|||
if (D & mask) |
|||
return false; |
|||
// check for duplicate bits (e.g. n-th character of the string is BOTH 'A' and 'B')
|
|||
if (D & set_bits) |
|||
return false; |
|||
set_bits |= D; |
|||
// check for sequences
|
|||
int_type tmp = D; |
|||
for (blockhash_len_t j = 0; j < digest_params::max_blockhash_sequence; j++) |
|||
{ |
|||
tmp <<= 1; |
|||
tmp &= D; |
|||
} |
|||
if (tmp) |
|||
return false; |
|||
} |
|||
// check if set_bits is now "all valid bits"
|
|||
if (len) |
|||
{ |
|||
mask = int_type(1u) << (len - 1); |
|||
mask = mask | (mask - 1); |
|||
} |
|||
else |
|||
{ |
|||
mask = 0; |
|||
} |
|||
if (mask != set_bits) |
|||
return false; |
|||
return true; |
|||
} |
|||
bool is_natural_blockhash_position_array(const pa_type& parray, blockhash_len_t len) const noexcept |
|||
{ |
|||
if (!IsAlphabetRestricted) |
|||
{ |
|||
// check only if IsAlphabetRestricted is false
|
|||
// (represented by regular characters and non Base64 characters are possible)
|
|||
int_type mask, set_bits; |
|||
if (len) |
|||
{ |
|||
mask = int_type(1u) << (len - 1); |
|||
mask = mask | (mask - 1); |
|||
} |
|||
else |
|||
{ |
|||
set_bits = 0; |
|||
} |
|||
for (size_t i = 0; i < 64; i++) |
|||
set_bits |= parray[base64::values[i]]; |
|||
// check if all valid bits are set by valid Base64 chareacters
|
|||
if (mask != set_bits) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
public: |
|||
bool is_valid(void) const noexcept |
|||
{ |
|||
if (blkhash1_len > digest_params::max_blockhash_len) |
|||
return false; |
|||
if (blkhash2_len > digest_params::max_blockhash_len) |
|||
return false; |
|||
if (blksize > 0xfffffffful) |
|||
return false; |
|||
// Note: digest_position_array_base::is_valid takes time.
|
|||
if (!is_valid_blockhash_position_array(blkhash1, blkhash1_len)) |
|||
return false; |
|||
if (!is_valid_blockhash_position_array(blkhash2, blkhash2_len)) |
|||
return false; |
|||
return true; |
|||
} |
|||
bool is_natural(void) const noexcept |
|||
{ |
|||
if (!is_valid()) |
|||
return false; |
|||
if (blkhash1_len < blkhash2_len) |
|||
return false; |
|||
if (!digest_blocksize::is_natural(blksize)) |
|||
return false; |
|||
if (!digest_blocksize::is_safe_to_double(blksize) && blkhash2_len >= 2) |
|||
return false; |
|||
// Note: digest_position_array_base::is_natural takes time.
|
|||
if (!is_natural_blockhash_position_array(blkhash1, blkhash1_len)) |
|||
return false; |
|||
if (!is_natural_blockhash_position_array(blkhash2, blkhash2_len)) |
|||
return false; |
|||
return true; |
|||
} |
|||
bool is_blocksize_natural(void) const noexcept |
|||
{ |
|||
return digest_blocksize::is_natural(blksize); |
|||
} |
|||
|
|||
// Comparison
|
|||
public: |
|||
static bool is_eq_except_blocksize( |
|||
const digest_position_array_base& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blkhash1_len != b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len != b.blkhash2_len) |
|||
return false; |
|||
int_type data_a = a.bitmap_data(); |
|||
int_type data_b = b.bitmap_data(); |
|||
for (size_t i = 0; i < pa_type::array_size; i++) |
|||
{ |
|||
if (data_a[i] != data_b[i]) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
static bool is_eq( |
|||
const digest_position_array_base& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
if (a.blksize != b.blksize) |
|||
return false; |
|||
return is_eq_except_blocksize(a, b); |
|||
} |
|||
template <bool IsShort> |
|||
static bool is_eq_except_blocksize( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blkhash1_len != b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len != b.blkhash2_len) |
|||
return false; |
|||
if (a.blksize != b.blksize) |
|||
return false; |
|||
const char* p = b.digest; |
|||
for (blockhash_len_t i = 0; i < b.blkhash1_len; i++) |
|||
{ |
|||
if (!(a.blkhash1[*p++] & (int_type(1u) << i))) |
|||
return false; |
|||
} |
|||
for (blockhash_len_t i = 0; i < b.blkhash2_len; i++) |
|||
{ |
|||
if (!(a.blkhash2[*p++] & (int_type(1u) << i))) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
template <bool IsShort> |
|||
static bool is_eq( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
if (a.blksize != b.blksize) |
|||
return false; |
|||
return is_eq_except_blocksize(a, b); |
|||
} |
|||
public: |
|||
friend bool operator==(const digest_position_array_base& a, const digest_position_array_base& b) noexcept { return is_eq(a, b); } |
|||
friend bool operator!=(const digest_position_array_base& a, const digest_position_array_base& b) noexcept { return !is_eq(a, b); } |
|||
public: |
|||
template <bool IsShort> |
|||
friend bool operator==( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
return is_eq(a, b); |
|||
} |
|||
template <bool IsShort> |
|||
friend bool operator!=( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
return !is_eq(a, b); |
|||
} |
|||
template <bool IsShort> |
|||
friend bool operator==( |
|||
const digest_data<IsAlphabetRestricted, IsShort>& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
return is_eq(b, a); |
|||
} |
|||
template <bool IsShort> |
|||
friend bool operator!=( |
|||
const digest_data<IsAlphabetRestricted, IsShort>& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
return !is_eq(b, a); |
|||
} |
|||
|
|||
// Friend classes
|
|||
template <comparison_version> friend class internal::digest_comparison_base; |
|||
template <comparison_version> friend class digest_comparison; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
rolling_hash.hpp |
|||
Rolling hash implementation (without known bugs) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_ROLLING_HASH_HPP |
|||
#define FFUZZYPP_ROLLING_HASH_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class rolling_hash |
|||
{ |
|||
public: |
|||
static constexpr const uint_least32_t window_size = 7; |
|||
static_assert(window_size != 0, "window_size must not be zero."); |
|||
protected: |
|||
uint_least32_t h1, h2, h3; |
|||
uint_least32_t n; |
|||
unsigned char window[window_size]; |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
h1 = h2 = h3 = n = 0; |
|||
for (size_t i = 0; i < window_size; i++) |
|||
window[i] = 0; |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
uint_least32_t C(c); |
|||
h2 = h2 - h1 + window_size * C; |
|||
h1 = h1 + C - window[n]; |
|||
h3 = (h3 << 5) ^ C; |
|||
window[n] = c; |
|||
n++; |
|||
if (n == window_size) |
|||
n = 0; |
|||
} |
|||
uint_least32_t sum(void) const noexcept |
|||
{ |
|||
return (h1 + h2 + h3) & uint_least32_t(0xfffffffful); |
|||
} |
|||
public: |
|||
rolling_hash(void) noexcept |
|||
{ |
|||
reset(); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,87 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
rolling_hash_ssdeep.hpp |
|||
Rolling hash implementation (with known bug in ssdeep) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_ROLLING_HASH_SSDEEP_HPP |
|||
#define FFUZZYPP_ROLLING_HASH_SSDEEP_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include "rolling_hash.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
// rolling_hash with known rolling-hash bug on ssdeep
|
|||
class rolling_hash_ssdeep |
|||
{ |
|||
public: |
|||
static constexpr const uint_least32_t window_size = rolling_hash::window_size; |
|||
private: |
|||
uint_least32_t h1, h2, h3; |
|||
uint_least32_t n; |
|||
unsigned char window[window_size]; |
|||
public: |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
// emulate bug on ssdeep
|
|||
uint_least32_t C(c); |
|||
h2 = h2 - h1 + window_size * C; |
|||
h1 = h1 + C - window[n % window_size]; |
|||
h3 = (h3 << 5) ^ C; |
|||
window[n % window_size] = c; |
|||
n++; |
|||
if ((n & uint_least32_t(0xfffffffful)) == 0) |
|||
n = 0; |
|||
} |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
h1 = h2 = h3 = n = 0; |
|||
for (size_t i = 0; i < window_size; i++) |
|||
window[i] = 0; |
|||
} |
|||
uint_least32_t sum(void) const noexcept |
|||
{ |
|||
return (h1 + h2 + h3) & uint_least32_t(0xfffffffful); |
|||
} |
|||
public: |
|||
rolling_hash_ssdeep(void) noexcept |
|||
{ |
|||
reset(); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,345 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/common_substr.hpp |
|||
Utility to find common substring in two strings |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS (HASHARRAY ALGORITHM) |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
CREDIT OF ADDED PORTIONS (BIT-PARALLEL ALGORITHM) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_COMMON_SUBSTR_HPP |
|||
#define FFUZZYPP_STRINGS_COMMON_SUBSTR_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
#include <cstring> |
|||
|
|||
#include <limits> |
|||
#include <type_traits> |
|||
|
|||
#include "../rolling_hash.hpp" |
|||
#include "position_array.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <size_t SubstrSize> |
|||
class common_substr_hasharray_impl |
|||
{ |
|||
private: |
|||
common_substr_hasharray_impl(void) = delete; |
|||
common_substr_hasharray_impl(const common_substr_hasharray_impl&) = delete; |
|||
public: |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size >= rolling_hash::window_size, |
|||
"substr_size must be equal or greater than window_size."); |
|||
public: |
|||
static bool match_long_buf( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len, |
|||
uint_least32_t* hashes_buf |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1); |
|||
assert(s2); |
|||
assert(s1len >= substr_size); |
|||
assert(s2len >= substr_size); |
|||
#endif |
|||
rolling_hash r; |
|||
// compute rolling hashes for each index of s1
|
|||
for (size_t i = 0; i < substr_size - 1; i++) |
|||
r.update(static_cast<unsigned char>(s1[i])); |
|||
for (size_t i = substr_size - 1; i < s1len; i++) |
|||
{ |
|||
r.update(static_cast<unsigned char>(s1[i])); |
|||
hashes_buf[i - (substr_size - 1)] = r.sum(); |
|||
} |
|||
s1len -= (substr_size - 1); |
|||
// compute rolling hashes for each index of s2
|
|||
r.reset(); |
|||
for (size_t j = 0; j < substr_size - 1; j++) |
|||
r.update(static_cast<unsigned char>(s2[j])); |
|||
for (size_t j = 0; j < s2len - (substr_size - 1); j++) |
|||
{ |
|||
r.update(static_cast<unsigned char>(s2[j + (substr_size - 1)])); |
|||
uint_least32_t h = r.sum(); |
|||
for (size_t i = 0; i < s1len; i++) |
|||
{ |
|||
// make sure we actually have common substring if hash matches
|
|||
if (hashes_buf[i] == h && !memcmp(s1 + i, s2 + j, substr_size)) |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
template < |
|||
size_t SubstrSize, |
|||
size_t MaxSize = std::numeric_limits<size_t>::max() |
|||
> |
|||
class common_substr_bitparallel_impl |
|||
{ |
|||
private: |
|||
common_substr_bitparallel_impl(void) = delete; |
|||
common_substr_bitparallel_impl(const common_substr_bitparallel_impl&) = delete; |
|||
public: |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(0 < max_size, "max_size must be nonzero."); |
|||
public: |
|||
template <typename TBitmap, char CMin, char CMax> |
|||
static bool match( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typedef position_array<TBitmap, char, CMin, CMax> pa_type; |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s2); |
|||
assert(s2len >= substr_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
// position array is too short to have such substrings
|
|||
if (pa_type::max_strlen < substr_size) |
|||
return false; |
|||
size_t r = substr_size - 1; |
|||
while (true) |
|||
{ |
|||
size_t l = r - (substr_size - 1); |
|||
// we must reverse s2 because bitmap of s1 is reversed.
|
|||
const char* ch = &s2[s2len - 1 - r]; |
|||
TBitmap D = s1[*ch]; |
|||
while (D) |
|||
{ |
|||
r--; |
|||
D = (D << 1) & s1[*++ch]; |
|||
if (r == l && D) |
|||
return true; |
|||
} |
|||
// Boyer-Moore-like skipping
|
|||
if (max_size - 1 <= std::numeric_limits<size_t>::max() - substr_size) |
|||
{ |
|||
// (max_size - 1) + substr_size <= max(size_t)
|
|||
r += substr_size; |
|||
if (r >= s2len) |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
// (max_size - 1) + substr_size > max(size_t)
|
|||
// which means we cannot safely perform r += substr_size first (very unlikely)
|
|||
if (r >= s2len - substr_size) |
|||
break; |
|||
r += substr_size; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
template <size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_hasharray |
|||
{ |
|||
private: |
|||
common_substr_hasharray(void) = delete; |
|||
common_substr_hasharray(const common_substr_hasharray&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size <= max_size, "substring size must not be greater than the maximum size."); |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1); |
|||
assert(s2); |
|||
assert(s1len <= max_size); |
|||
#endif |
|||
uint_least32_t hashes[max_size - (substr_size - 1)]; |
|||
if (s1len < substr_size) |
|||
return false; |
|||
if (s2len < substr_size) |
|||
return false; |
|||
return internal |
|||
::common_substr_hasharray_impl<substr_size> |
|||
::match_long_buf(s1, s1len, s2, s2len, hashes); |
|||
} |
|||
}; |
|||
|
|||
template <typename TBitmap, char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_bitparallel |
|||
{ |
|||
private: |
|||
common_substr_bitparallel(void) = delete; |
|||
common_substr_bitparallel(const common_substr_bitparallel&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size <= max_size, "substring size must not be greater than the maximum size."); |
|||
static_assert(max_size <= position_array<TBitmap, char, CMin, CMax>::max_strlen, |
|||
"max_size must not be greater than max_strlen of corresponding position_matrix for efficiency."); |
|||
public: |
|||
static bool match( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s2); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
if (s2len < substr_size) |
|||
return false; |
|||
return internal |
|||
::common_substr_bitparallel_impl<substr_size, max_size> |
|||
::match(s1, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename TBitmap, char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_bitparallel_wrapper |
|||
{ |
|||
private: |
|||
common_substr_bitparallel_wrapper(void) = delete; |
|||
common_substr_bitparallel_wrapper(const common_substr_bitparallel_wrapper&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size <= max_size, "substring size must not be greater than the maximum size."); |
|||
static_assert(max_size <= position_array<TBitmap, char, CMin, CMax>::max_strlen, |
|||
"max_size must not be greater than max_strlen of corresponding position_matrix for efficiency."); |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s2); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
if (s2len < substr_size) |
|||
return false; |
|||
position_array<TBitmap, char, CMin, CMax> parray(s1, s1len); |
|||
return internal |
|||
::common_substr_bitparallel_impl<substr_size, max_size> |
|||
::match(parray, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <char CMin, char CMax, size_t MaxSize, size_t SubstrSize, bool IsPositionArrayAvailable> |
|||
class common_substr_impl_selector; |
|||
|
|||
template <char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_impl_selector<CMin, CMax, MaxSize, SubstrSize, true> |
|||
{ |
|||
private: |
|||
common_substr_impl_selector(void) = delete; |
|||
common_substr_impl_selector(const common_substr_impl_selector&) = delete; |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typename auto_position_array<MaxSize, char, CMin, CMax>::type parray(s1, s1len); |
|||
return common_substr_bitparallel<typename decltype(parray)::bitmap_type, |
|||
CMin, CMax, MaxSize, SubstrSize>::match(parray, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_impl_selector<CMin, CMax, MaxSize, SubstrSize, false> |
|||
{ |
|||
private: |
|||
common_substr_impl_selector(void) = delete; |
|||
common_substr_impl_selector(const common_substr_impl_selector&) = delete; |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return common_substr_hasharray<MaxSize, SubstrSize>::match(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
|
|||
|
|||
template <size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_fast |
|||
{ |
|||
private: |
|||
common_substr_fast(void) = delete; |
|||
common_substr_fast(const common_substr_fast&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
public: |
|||
template < |
|||
char CMin = std::numeric_limits<char>::min(), |
|||
char CMax = std::numeric_limits<char>::max() |
|||
> |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return internal::common_substr_impl_selector< |
|||
CMin, CMax, max_size, substr_size, |
|||
is_auto_position_array_available<MaxSize, char, CMin, CMax>::value |
|||
>::match(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,521 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/edit_dist.hpp |
|||
Indel distance implementation |
|||
|
|||
|
|||
Copyright (C) 2014 kikairoya <kikairoya@gmail.com> |
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
This program can be used, redistributed or modified under any of |
|||
Boost Software License 1.0, GPL v2 or GPL v3 |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_EDIT_DIST_HPP |
|||
#define FFUZZYPP_STRINGS_EDIT_DIST_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
|
|||
#include <algorithm> |
|||
#include <limits> |
|||
#include <utility> |
|||
|
|||
#include "../utils/safe_int.hpp" |
|||
#include "position_array.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename Tcost> |
|||
class edit_dist_dp_impl |
|||
{ |
|||
private: |
|||
edit_dist_dp_impl(void) = delete; |
|||
edit_dist_dp_impl(const edit_dist_dp_impl&) = delete; |
|||
public: |
|||
typedef Tcost cost_type; |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<cost_type>(), |
|||
"cost_type must be an unsigned integral type."); |
|||
private: |
|||
static void update_cost_inner( |
|||
const char* s1, |
|||
const char* s2, size_t s2len, |
|||
size_t i, |
|||
cost_type* &row1, cost_type* &row2 |
|||
) noexcept |
|||
{ |
|||
row2[0] = static_cast<cost_type>(i) + 1; |
|||
for (size_t j = 0; j < s2len; j++) |
|||
{ |
|||
cost_type cost_a = row1[j+1] + 1; |
|||
cost_type cost_d = row2[j] + 1; |
|||
cost_type cost_r = row1[j] + (s1[i] == s2[j] ? 0 : 2); |
|||
row2[j+1] = std::min(std::min(cost_a, cost_d), cost_r); |
|||
} |
|||
std::swap(row1, row2); |
|||
} |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len, |
|||
cost_type* row1, cost_type* row2 |
|||
) noexcept |
|||
{ |
|||
for (size_t j = 0; j <= s2len; j++) |
|||
row1[j] = static_cast<cost_type>(j); |
|||
for (size_t i = 0; i < s1len; i++) |
|||
update_cost_inner(s1, s2, s2len, i, row1, row2); |
|||
return row1[s2len]; |
|||
} |
|||
static cost_type cost_nonempty( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len, |
|||
cost_type* row1, cost_type* row2 |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len > 0); |
|||
assert(s2len > 0); |
|||
#endif |
|||
row1[0] = 1; |
|||
for (size_t j = 0; j < s2len; j++) |
|||
{ |
|||
cost_type cost_d = row1[j] + 1; |
|||
cost_type cost_r = static_cast<cost_type>(j) + (s1[0] == s2[j] ? 0 : 2); |
|||
row1[j+1] = std::min(cost_d, cost_r); |
|||
} |
|||
for (size_t i = 1; i < s1len; i++) |
|||
update_cost_inner(s1, s2, s2len, i, row1, row2); |
|||
return row1[s2len]; |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_bitparallel_impl |
|||
{ |
|||
private: |
|||
edit_dist_bitparallel_impl(void) = delete; |
|||
edit_dist_bitparallel_impl(const edit_dist_bitparallel_impl&) = delete; |
|||
public: |
|||
typedef Tcost cost_type; |
|||
public: |
|||
template <typename TBitmap, char CMin, char CMax> |
|||
static cost_type cost_nonempty( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
cost_type cur = s1len; |
|||
TBitmap msb = TBitmap(1ull) << (s1len - 1); |
|||
TBitmap pv = -1; |
|||
TBitmap nv = 0; |
|||
for (size_t i = 0; i < s2len; i++) |
|||
{ |
|||
TBitmap mt = s1[s2[i]]; |
|||
TBitmap zd = (((mt & pv) + pv) ^ pv) | mt | nv; |
|||
TBitmap nh = pv & zd; |
|||
if (nh & msb) |
|||
--cur; |
|||
TBitmap x = nv | ~(pv | zd) | (pv & ~mt & TBitmap(1ull)); |
|||
TBitmap y = (pv - nh) >> 1; |
|||
/*
|
|||
i-th bit of ph does not depend on i-th bit of y |
|||
(only upper bits of ph are affected). |
|||
So, ph does not depend on invalid bit in y. |
|||
*/ |
|||
TBitmap ph = (x + y) ^ y; |
|||
if (ph & msb) |
|||
++cur; |
|||
TBitmap t = (ph << 1) + TBitmap(1ull); |
|||
nv = t & zd; |
|||
pv = (nh << 1) | ~(t | zd) | (t & (pv - nh)); |
|||
} |
|||
return cur; |
|||
} |
|||
template <typename TBitmap, char CMin, char CMax> |
|||
static cost_type cost( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return s1len == 0 |
|||
? s2len |
|||
: cost_nonempty(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_dp |
|||
{ |
|||
private: |
|||
edit_dist_dp(void) = delete; |
|||
edit_dist_dp(const edit_dist_dp&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
cost_type rows[2][max_size + 1]; |
|||
return internal::edit_dist_dp_impl<cost_type>::cost(s1, s1len, s2, s2len, rows[0], rows[1]); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_nonempty_dp |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_dp(void) = delete; |
|||
edit_dist_nonempty_dp(const edit_dist_nonempty_dp&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
cost_type rows[2][max_size + 1]; |
|||
return internal::edit_dist_dp_impl<cost_type>::cost_nonempty(s1, s1len, s2, s2len, rows[0], rows[1]); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_bitparallel |
|||
{ |
|||
private: |
|||
edit_dist_bitparallel(void) = delete; |
|||
edit_dist_bitparallel(const edit_dist_bitparallel&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_bitparallel_wrapper |
|||
{ |
|||
private: |
|||
edit_dist_bitparallel_wrapper(void) = delete; |
|||
edit_dist_bitparallel_wrapper(const edit_dist_bitparallel_wrapper&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
position_array<TBitmap, char, CMin, CMax> parray(s1, s1len); |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost(parray, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_nonempty_bitparallel |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_bitparallel(void) = delete; |
|||
edit_dist_nonempty_bitparallel(const edit_dist_nonempty_bitparallel&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost_nonempty(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_nonempty_bitparallel_wrapper |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_bitparallel_wrapper(void) = delete; |
|||
edit_dist_nonempty_bitparallel_wrapper(const edit_dist_nonempty_bitparallel_wrapper&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
position_array<TBitmap, char, CMin, CMax> parray(s1, s1len); |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost_nonempty(parray, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename Tcost, char CMin, char CMax, size_t MaxSize, bool IsPositionArrayAvailable> |
|||
class edit_dist_impl_selector; |
|||
|
|||
template <typename Tcost, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_impl_selector<Tcost, CMin, CMax, MaxSize, true> |
|||
{ |
|||
private: |
|||
edit_dist_impl_selector(void) = delete; |
|||
edit_dist_impl_selector(const edit_dist_impl_selector&) = delete; |
|||
public: |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typename auto_position_array<MaxSize, char, CMin, CMax>::type parray(s1, s1len); |
|||
return edit_dist_bitparallel<Tcost, |
|||
typename decltype(parray)::bitmap_type, CMin, CMax, MaxSize>::cost(parray, s1len, s2, s2len); |
|||
} |
|||
static Tcost cost_nonempty( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typename auto_position_array<MaxSize, char, CMin, CMax>::type parray(s1, s1len); |
|||
return edit_dist_nonempty_bitparallel<Tcost, |
|||
typename decltype(parray)::bitmap_type, CMin, CMax, MaxSize>::cost(parray, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_impl_selector<Tcost, CMin, CMax, MaxSize, false> |
|||
{ |
|||
private: |
|||
edit_dist_impl_selector(void) = delete; |
|||
edit_dist_impl_selector(const edit_dist_impl_selector&) = delete; |
|||
public: |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return edit_dist_dp<Tcost, MaxSize>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
static Tcost cost_nonempty( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return edit_dist_nonempty_dp<Tcost, MaxSize>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
|
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_fast |
|||
{ |
|||
private: |
|||
edit_dist_fast(void) = delete; |
|||
edit_dist_fast(const edit_dist_fast&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
typedef Tcost cost_type; |
|||
public: |
|||
template < |
|||
char CMin = std::numeric_limits<char>::min(), |
|||
char CMax = std::numeric_limits<char>::max() |
|||
> |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return internal::edit_dist_impl_selector< |
|||
cost_type, CMin, CMax, max_size, |
|||
is_auto_position_array_available<MaxSize, char, CMin, CMax>::value |
|||
>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_nonempty_fast |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_fast(void) = delete; |
|||
edit_dist_nonempty_fast(const edit_dist_nonempty_fast&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
typedef Tcost cost_type; |
|||
public: |
|||
template < |
|||
char CMin = std::numeric_limits<char>::min(), |
|||
char CMax = std::numeric_limits<char>::max() |
|||
> |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return internal::edit_dist_impl_selector< |
|||
cost_type, CMin, CMax, max_size, |
|||
is_auto_position_array_available<MaxSize, char, CMin, CMax>::value |
|||
>::cost_nonempty(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
template <typename Tedit_dist> |
|||
class edit_dist_norm |
|||
{ |
|||
private: |
|||
edit_dist_norm(void) = delete; |
|||
edit_dist_norm(const edit_dist_norm&) = delete; |
|||
public: |
|||
typedef typename Tedit_dist::cost_type cost_type; |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
// Normalization: make s1 short for better effficiency
|
|||
if (s1len <= s2len) |
|||
return Tedit_dist::cost(s1, s1len, s2, s2len); |
|||
else |
|||
return Tedit_dist::cost(s2, s2len, s1, s1len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tedit_dist> |
|||
class edit_dist_norm_rev |
|||
{ |
|||
private: |
|||
edit_dist_norm_rev(void) = delete; |
|||
edit_dist_norm_rev(const edit_dist_norm_rev&) = delete; |
|||
public: |
|||
typedef typename Tedit_dist::cost_type cost_type; |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
// Reverse normalization: make s2 short for (possibly) worst effficiency
|
|||
if (s1len <= s2len) |
|||
return Tedit_dist::cost(s2, s2len, s1, s1len); |
|||
else |
|||
return Tedit_dist::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,82 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/nosequences.hpp |
|||
String utilities with same interface as sequence utilities |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_NOSEQUENCES_HPP |
|||
#define FFUZZYPP_STRINGS_NOSEQUENCES_HPP |
|||
|
|||
#include <cstddef> |
|||
|
|||
#include "terminators.hpp" |
|||
#include "transform.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
template <typename Ttransform = default_char_transform> |
|||
class nosequences |
|||
{ |
|||
private: |
|||
nosequences(void) = delete; |
|||
nosequences(const nosequences&) = delete; |
|||
public: |
|||
static constexpr bool has_sequences(const char* buf, size_t size) noexcept |
|||
{ |
|||
return false; |
|||
} |
|||
static void copy_raw(char* out, const char* in, size_t size) noexcept |
|||
{ |
|||
while (size--) |
|||
*out++ = Ttransform::transform(*in++); |
|||
} |
|||
static size_t copy_elim_sequences(char* out, const char* in, size_t size) noexcept |
|||
{ |
|||
copy_raw(out, in, size); |
|||
return size; |
|||
} |
|||
template <char... terms> |
|||
class string_copy |
|||
{ |
|||
private: |
|||
string_copy(void) = delete; |
|||
string_copy(const string_copy&) = delete; |
|||
public: |
|||
static bool copy_elim_sequences(char*& out, size_t outsize, const char*& in) noexcept |
|||
{ |
|||
while (outsize--) |
|||
{ |
|||
char ch; |
|||
if (terminators<terms...>::isterm(ch = *in)) |
|||
return true; |
|||
in++; |
|||
*out++ = Ttransform::transform(ch); |
|||
} |
|||
if (terminators<terms...>::isterm(*in)) |
|||
return true; |
|||
return false; |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,409 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/position_array.hpp |
|||
Position array (for bit-parallel algorithms) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_POSITION_ARRAY_HPP |
|||
#define FFUZZYPP_STRINGS_POSITION_ARRAY_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
#include <cstring> |
|||
|
|||
#include <limits> |
|||
#include <type_traits> |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename T> |
|||
class digits_on_size |
|||
{ |
|||
private: |
|||
digits_on_size(void) = delete; |
|||
digits_on_size(const digits_on_size&) = delete; |
|||
public: |
|||
static constexpr const size_t size = |
|||
std::numeric_limits<T>::digits <= std::numeric_limits<size_t>::max() |
|||
? std::numeric_limits<T>::digits |
|||
: std::numeric_limits<size_t>::max(); |
|||
}; |
|||
|
|||
/*
|
|||
Template to return guaranteed bit length on unsigned integral types: |
|||
General template works only for binary (radix==2) types and |
|||
specialized templates work for radix!=2 binary types. |
|||
*/ |
|||
template <typename T> |
|||
class guaranteed_bitlen |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
static_assert(std::is_integral<T>::value, "T must be an integral type."); |
|||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned integral type."); |
|||
static_assert(std::numeric_limits<T>::radix == 2, "T must be a binary unsigned integral type."); |
|||
public: |
|||
static constexpr const size_t size = digits_on_size<T>::size; |
|||
}; |
|||
|
|||
template <> |
|||
class guaranteed_bitlen<unsigned> |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
typedef std::numeric_limits<unsigned> limits_type; |
|||
public: |
|||
static constexpr const size_t size = |
|||
limits_type::radix == 2 && limits_type::digits < 16 |
|||
? digits_on_size<unsigned>::size : 16; |
|||
}; |
|||
|
|||
template <> |
|||
class guaranteed_bitlen<unsigned long> |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
typedef std::numeric_limits<unsigned long> limits_type; |
|||
public: |
|||
static constexpr const size_t size = |
|||
limits_type::radix == 2 && limits_type::digits < 32 |
|||
? digits_on_size<unsigned long>::size : 32; |
|||
}; |
|||
|
|||
template <> |
|||
class guaranteed_bitlen<unsigned long long> |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
typedef std::numeric_limits<unsigned long long> limits_type; |
|||
public: |
|||
static constexpr const size_t size = |
|||
limits_type::radix == 2 && limits_type::digits < 64 |
|||
? digits_on_size<unsigned long long>::size : 64; |
|||
}; |
|||
} |
|||
|
|||
class position_array_params |
|||
{ |
|||
private: |
|||
position_array_params(void) = delete; |
|||
position_array_params(const position_array_params&) = delete; |
|||
public: |
|||
#ifdef FFUZZYPP_DISABLE_POSITION_ARRAY |
|||
static constexpr const bool is_disabled_by_build = true; |
|||
#else |
|||
static constexpr const bool is_disabled_by_build = false; |
|||
#endif |
|||
static constexpr const size_t max_efficient_array_size = 1024; |
|||
}; |
|||
static_assert(position_array_params::max_efficient_array_size >= 64, |
|||
"position_array_params::max_efficient_array_size must not be less than 64."); |
|||
|
|||
template < |
|||
typename TBitmap, |
|||
typename TChar, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
class position_array_safety |
|||
{ |
|||
private: |
|||
position_array_safety(void) = delete; |
|||
position_array_safety(const position_array_safety&) = delete; |
|||
public: |
|||
static constexpr const bool is_welldefined = |
|||
std::is_integral<TBitmap>::value && |
|||
std::is_unsigned<TBitmap>::value && |
|||
std::is_same<TBitmap, decltype(TBitmap(0u) | TBitmap(0u))>::value && |
|||
std::is_integral<TChar>::value && |
|||
(std::numeric_limits<TBitmap>::radix == 2 || |
|||
std::is_same<TBitmap, unsigned>::value || |
|||
std::is_same<TBitmap, unsigned long>::value || |
|||
std::is_same<TBitmap, unsigned long long>::value |
|||
) && |
|||
(CMin <= CMax) && |
|||
(std::is_unsigned<TChar>::value |
|||
? CMax - CMin <= std::numeric_limits<size_t>::max() |
|||
: (0 <= CMin |
|||
? std::numeric_limits<intmax_t>::min() + intmax_t(CMin) <= intmax_t(CMax) |
|||
: intmax_t(CMax) <= std::numeric_limits<intmax_t>::max() + intmax_t(CMin) |
|||
) && intmax_t(CMax) - intmax_t(CMin) <= std::numeric_limits<size_t>::max() |
|||
) && |
|||
(size_t(CMax) - size_t(CMin) < std::numeric_limits<size_t>::max()) && |
|||
(sizeof(TBitmap) <= std::numeric_limits<size_t>::max() / (size_t(CMax) - size_t(CMin) + 1u)); |
|||
public: |
|||
static constexpr const bool is_considered_efficient = |
|||
!position_array_params::is_disabled_by_build && is_welldefined && |
|||
(size_t(CMax) - size_t(CMin) + 1u) <= position_array_params::max_efficient_array_size; |
|||
}; |
|||
|
|||
template < |
|||
typename TBitmap = unsigned long long, |
|||
typename TChar = char, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
class position_array |
|||
{ |
|||
private: |
|||
static_assert(std::is_integral<TBitmap>::value, "TBitmap must be an integral type."); |
|||
static_assert(std::is_unsigned<TBitmap>::value, "TBitmap must be an unsigned integral type."); |
|||
static_assert(std::is_same<TBitmap, decltype(TBitmap(0u) | TBitmap(0u))>::value, |
|||
"TBitmap must preserve its type after integral promotion."); |
|||
static_assert(std::is_integral<TChar>::value, "TChar must be an integral type."); |
|||
static_assert(CMin <= CMax, "CMin must not be greater than CMax."); |
|||
static_assert( |
|||
/*
|
|||
Because signed to unsigned conversion is well-defined, |
|||
this predicate also indicates whether size_t(CMax) - size_t(CMin) is valid. |
|||
Note that this predicate does not cover all the case where |
|||
char is signed and (CMin - CMax) is greater than the maximum value of intmax_t. |
|||
*/ |
|||
std::is_unsigned<TChar>::value |
|||
? CMax - CMin <= std::numeric_limits<size_t>::max() |
|||
: (0 <= CMin |
|||
? std::numeric_limits<intmax_t>::min() + intmax_t(CMin) <= intmax_t(CMax) |
|||
: intmax_t(CMax) <= std::numeric_limits<intmax_t>::max() + intmax_t(CMin) |
|||
) && intmax_t(CMax) - intmax_t(CMin) <= std::numeric_limits<size_t>::max(), |
|||
"(CMax - CMin) must be in range of both size_t and the widest type with same signedness as TChar."); |
|||
static_assert(size_t(CMax) - size_t(CMin) < std::numeric_limits<size_t>::max(), |
|||
"(size_t(CMax) - size_t(CMin) + 1) must be in range of size_t."); |
|||
static_assert(sizeof(TBitmap) <= std::numeric_limits<size_t>::max() / (size_t(CMax) - size_t(CMin) + 1u), |
|||
"(array_size * sizeof(TBitmap)) must be in range of size_t."); |
|||
public: |
|||
static constexpr const size_t array_size = size_t(CMax) - size_t(CMin) + 1u; |
|||
static constexpr const size_t max_strlen = internal::guaranteed_bitlen<TBitmap>::size; |
|||
typedef TBitmap bitmap_type; |
|||
typedef TChar char_type; |
|||
static constexpr const char_type char_min = CMin; |
|||
static constexpr const char_type char_max = CMax; |
|||
private: |
|||
bitmap_type bitmap[array_size]; |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
memset(bitmap, 0, sizeof(bitmap)); |
|||
} |
|||
void construct_noinit(const char_type* str, size_t len) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(len <= max_strlen); |
|||
#endif |
|||
for (size_t i = 0; i < len; i++) |
|||
{ |
|||
char_type ch = *str++; |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(char_min <= ch && ch <= char_max); |
|||
#endif |
|||
bitmap[size_t(ch) - size_t(char_min)] |= bitmap_type(1u) << i; |
|||
} |
|||
} |
|||
void construct_noinit_safe(const char_type* str, size_t len) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(len <= max_strlen); |
|||
#endif |
|||
for (size_t i = 0; i < len; i++) |
|||
{ |
|||
char_type ch = *str++; |
|||
if (ch < char_min || char_max < ch) |
|||
continue; |
|||
bitmap[size_t(ch) - size_t(char_min)] |= bitmap_type(1u) << i; |
|||
} |
|||
} |
|||
void construct(const char_type* str, size_t len) noexcept |
|||
{ |
|||
reset(); |
|||
construct_noinit(str, len); |
|||
} |
|||
void construct_safe(const char_type* str, size_t len) noexcept |
|||
{ |
|||
reset(); |
|||
construct_noinit_safe(str, len); |
|||
} |
|||
public: |
|||
bitmap_type bitmap_for(char_type ch) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(char_min <= ch && ch <= char_max); |
|||
#endif |
|||
return bitmap[size_t(ch) - size_t(char_min)]; |
|||
} |
|||
bitmap_type bitmap_for_safe(char_type ch) const noexcept |
|||
{ |
|||
if (ch < char_min || char_max < ch) |
|||
return bitmap_type(0u); |
|||
return bitmap[size_t(ch) - size_t(char_min)]; |
|||
} |
|||
bitmap_type operator[](char_type ch) const noexcept |
|||
{ |
|||
return bitmap_for(ch); |
|||
} |
|||
const bitmap_type* bitmap_data(void) const noexcept |
|||
{ |
|||
return bitmap; |
|||
} |
|||
public: |
|||
position_array(void) noexcept = default; // initialize to undefined state
|
|||
position_array(const position_array& other) noexcept |
|||
{ |
|||
if (bitmap != other.bitmap) |
|||
memcpy(bitmap, other.bitmap, sizeof(bitmap)); |
|||
} |
|||
const position_array& operator=(const position_array& other) noexcept |
|||
{ |
|||
if (bitmap != other.bitmap) |
|||
memcpy(bitmap, other.bitmap, sizeof(bitmap)); |
|||
return *this; |
|||
} |
|||
position_array(const char_type* str, size_t len) noexcept |
|||
{ |
|||
construct(str, len); |
|||
} |
|||
position_array(const char_type* str, size_t len, bool safe) noexcept |
|||
{ |
|||
if (safe) |
|||
construct_safe(str, len); |
|||
else |
|||
construct(str, len); |
|||
} |
|||
static void swap(position_array& a, position_array& b) noexcept |
|||
{ |
|||
std::swap(a.bitmap, b.bitmap); // C++11 version of swap
|
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax, typename TEnabler = void> |
|||
class auto_position_array_internal; |
|||
|
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_internal<MaxSize, TChar, CMin, CMax, typename std::enable_if<( |
|||
MaxSize > 0 && MaxSize <= 16 && position_array_safety<unsigned, TChar, CMin, CMax>::is_considered_efficient |
|||
)>::type> |
|||
{ |
|||
private: |
|||
auto_position_array_internal(void); |
|||
auto_position_array_internal(const auto_position_array_internal&) = delete; |
|||
public: |
|||
typedef unsigned int_type; |
|||
}; |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_internal<MaxSize, TChar, CMin, CMax, typename std::enable_if<( |
|||
MaxSize > 16 && MaxSize <= 32 && position_array_safety<unsigned long, TChar, CMin, CMax>::is_considered_efficient |
|||
)>::type> |
|||
{ |
|||
private: |
|||
auto_position_array_internal(void); |
|||
auto_position_array_internal(const auto_position_array_internal&) = delete; |
|||
public: |
|||
typedef unsigned long int_type; |
|||
}; |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_internal<MaxSize, TChar, CMin, CMax, typename std::enable_if<( |
|||
MaxSize > 32 && MaxSize <= 64 && position_array_safety<unsigned long long, TChar, CMin, CMax>::is_considered_efficient |
|||
)>::type> |
|||
{ |
|||
private: |
|||
auto_position_array_internal(void); |
|||
auto_position_array_internal(const auto_position_array_internal&) = delete; |
|||
public: |
|||
typedef unsigned long long int_type; |
|||
}; |
|||
} |
|||
|
|||
// Predicate to test if automatically-chosen position array available
|
|||
template < |
|||
size_t MaxSize, |
|||
typename TChar = char, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
struct is_auto_position_array_available |
|||
: std::integral_constant<bool, ( |
|||
(MaxSize > 0 && MaxSize <= 16 && position_array_safety<unsigned, TChar, CMin, CMax>::is_considered_efficient) || |
|||
(MaxSize > 16 && MaxSize <= 32 && position_array_safety<unsigned long, TChar, CMin, CMax>::is_considered_efficient) || |
|||
(MaxSize > 32 && MaxSize <= 64 && position_array_safety<unsigned long long, TChar, CMin, CMax>::is_considered_efficient) |
|||
)> |
|||
{ }; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax, bool> |
|||
class auto_position_array_impl; |
|||
|
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_impl<MaxSize, TChar, CMin, CMax, true> |
|||
{ |
|||
private: |
|||
auto_position_array_impl(void) = delete; |
|||
auto_position_array_impl(const auto_position_array_impl&) = delete; |
|||
public: |
|||
typedef typename auto_position_array_internal<MaxSize, TChar, CMin, CMax>::int_type int_type; |
|||
typedef position_array<int_type, TChar, CMin, CMax> type; |
|||
}; |
|||
|
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_impl<MaxSize, TChar, CMin, CMax, false> |
|||
{ |
|||
private: |
|||
auto_position_array_impl(void) = delete; |
|||
auto_position_array_impl(const auto_position_array_impl&) = delete; |
|||
}; |
|||
} |
|||
|
|||
// Automatically-chosen position array
|
|||
template < |
|||
size_t MaxSize, |
|||
typename TChar = char, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
class auto_position_array |
|||
: public internal::auto_position_array_impl< |
|||
MaxSize, TChar, CMin, CMax, |
|||
is_auto_position_array_available<MaxSize, TChar, CMin, CMax>::value |
|||
> |
|||
{ |
|||
private: |
|||
auto_position_array(void) = delete; |
|||
auto_position_array(const auto_position_array&) = delete; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,154 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/sequences.hpp |
|||
Utilities to find/eliminate sequences of same characters |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_SEQUENCES_HPP |
|||
#define FFUZZYPP_STRINGS_SEQUENCES_HPP |
|||
|
|||
#include <cstddef> |
|||
|
|||
#include "terminators.hpp" |
|||
#include "transform.hpp" |
|||
#include "../utils/safe_int.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
template <size_t MaxSequenceSize, typename Ttransform = default_char_transform> |
|||
class sequences |
|||
{ |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, MaxSequenceSize>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::value, |
|||
"MaxSequenceSize + 1 must be in range of size_t."); |
|||
private: |
|||
sequences(void) = delete; |
|||
sequences(const sequences&) = delete; |
|||
public: |
|||
static constexpr const size_t max_sequence_size = MaxSequenceSize; |
|||
static_assert(max_sequence_size != 0, "max_sequence_size must not be zero."); |
|||
public: |
|||
static bool has_sequences(const char* buf, size_t size) noexcept |
|||
{ |
|||
if (size <= max_sequence_size) |
|||
return false; |
|||
size_t seq = 0; |
|||
char prev = *buf++; |
|||
while (--size) |
|||
{ |
|||
char curr = *buf++; |
|||
if (curr == prev) |
|||
{ |
|||
if (++seq == max_sequence_size) |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
seq = 0; |
|||
prev = curr; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
static size_t copy_elim_sequences(char* out, const char* in, size_t size) noexcept |
|||
{ |
|||
if (size <= max_sequence_size) |
|||
{ |
|||
for (size_t i = 0; i < size; i++) |
|||
*out++ = Ttransform::transform(*in++); |
|||
return size; |
|||
} |
|||
size_t csz = 1; |
|||
size_t seq = 0; |
|||
char prev = *in++; |
|||
*out++ = Ttransform::transform(prev); |
|||
while (--size) |
|||
{ |
|||
char curr = *in++; |
|||
if (curr == prev) |
|||
{ |
|||
if (++seq >= max_sequence_size) |
|||
{ |
|||
seq = max_sequence_size; |
|||
continue; |
|||
} |
|||
*out++ = Ttransform::transform(curr); csz++; |
|||
} |
|||
else |
|||
{ |
|||
*out++ = Ttransform::transform(curr); csz++; |
|||
seq = 0; prev = curr; |
|||
} |
|||
} |
|||
return csz; |
|||
} |
|||
template <char... terms> |
|||
class string_copy |
|||
{ |
|||
private: |
|||
string_copy(void) = delete; |
|||
string_copy(const string_copy&) = delete; |
|||
public: |
|||
static bool copy_elim_sequences(char*& out, size_t outsize, const char*& in) noexcept |
|||
{ |
|||
size_t seq = 0; |
|||
char prev = *in; |
|||
if (terminators<terms...>::isterm(prev)) |
|||
return true; |
|||
if (outsize == 0) |
|||
return false; |
|||
*out++ = Ttransform::transform(prev); in++; outsize--; |
|||
while (true) |
|||
{ |
|||
char curr = *in; |
|||
if (terminators<terms...>::isterm(curr)) |
|||
return true; |
|||
in++; |
|||
if (curr == prev) |
|||
{ |
|||
if (++seq >= max_sequence_size) |
|||
{ |
|||
seq = max_sequence_size; |
|||
continue; |
|||
} |
|||
if (outsize == 0) |
|||
return false; |
|||
*out++ = Ttransform::transform(curr); outsize--; |
|||
} |
|||
else |
|||
{ |
|||
if (outsize == 0) |
|||
return false; |
|||
*out++ = Ttransform::transform(curr); outsize--; |
|||
seq = 0; prev = curr; |
|||
} |
|||
} |
|||
// unreachable
|
|||
return false; |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,58 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/terminators.hpp |
|||
Terminator characters |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_TERMINATORS_HPP |
|||
#define FFUZZYPP_STRINGS_TERMINATORS_HPP |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
template <char...> class terminators; |
|||
template <> |
|||
class terminators<> |
|||
{ |
|||
private: |
|||
terminators(void) = delete; |
|||
terminators(const terminators&) = delete; |
|||
public: |
|||
static bool isterm(char ch) noexcept |
|||
{ |
|||
return !ch; |
|||
} |
|||
}; |
|||
template <char c, char... rem> |
|||
class terminators<c, rem...> |
|||
{ |
|||
private: |
|||
terminators(void) = delete; |
|||
terminators(const terminators&) = delete; |
|||
public: |
|||
static bool isterm(char ch) noexcept |
|||
{ |
|||
return terminators<rem...>::isterm(ch) || ch == c; |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,44 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/transform.hpp |
|||
String transformation (char-to-char mapping) utility |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_TRANSFORM_HPP |
|||
#define FFUZZYPP_STRINGS_TRANSFORM_HPP |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
class default_char_transform |
|||
{ |
|||
private: |
|||
default_char_transform(void) = delete; |
|||
default_char_transform(const default_char_transform&) = delete; |
|||
public: |
|||
static constexpr char transform(char ch) noexcept |
|||
{ |
|||
return ch; |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,60 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
likely.hpp |
|||
Branch prediction control macros |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_LIKELY_HPP |
|||
#define FFUZZYPP_UTILS_LIKELY_HPP |
|||
|
|||
#define FFUZZYPP_LIKELY_PORTABLE(x) (!!(x)) |
|||
|
|||
#ifndef FFUZZYPP_DISABLE_COMPILER_BUILTINS |
|||
#if !defined(__GNUC__) |
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#elif __GNUC__ < 2 |
|||
// DISABLE: GNU compiler version < 2.0
|
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#elif __GNUC__ >= 3 |
|||
// ENABLE: GNU compiler version >= 3.0
|
|||
#elif !defined(__GNUC_MINOR__) |
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#elif __GNUC_MINOR__ <= 95 |
|||
// DISABLE: GNU compiler version <= 2.95
|
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#endif |
|||
#endif |
|||
|
|||
#ifdef FFUZZYPP_LIKELY |
|||
#undef FFUZZYPP_LIKELY |
|||
#endif |
|||
#ifdef FFUZZYPP_UNLIKELY |
|||
#undef FFUZZYPP_UNLIKELY |
|||
#endif |
|||
|
|||
#ifdef FFUZZYPP_DISABLE_COMPILER_BUILTINS |
|||
#define FFUZZYPP_LIKELY FFUZZYPP_LIKELY_PORTABLE |
|||
#define FFUZZYPP_UNLIKELY FFUZZYPP_LIKELY_PORTABLE |
|||
#else |
|||
#define FFUZZYPP_LIKELY(x) (__builtin_expect(!!(x), 1)) |
|||
#define FFUZZYPP_UNLIKELY(x) (__builtin_expect(!!(x), 0)) |
|||
#endif |
|||
|
|||
#endif |
@ -0,0 +1,67 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
minmax.hpp |
|||
C++14-compatible minimum / maximum library |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_MINMAX_HPP |
|||
#define FFUZZYPP_UTILS_MINMAX_HPP |
|||
|
|||
/*
|
|||
std::min and std::max in C++11 are not marked constexpr. |
|||
On the other hand, C++14's std::min and std::max are marked constexpr. |
|||
|
|||
This library implements constexpr min and max in C++11 |
|||
(but compatible with C++14). |
|||
|
|||
However, we won't implement initializer_list variant of |
|||
min and max since we aren't using it. |
|||
*/ |
|||
|
|||
namespace ffuzzy { |
|||
namespace minmax { |
|||
|
|||
template <class T> |
|||
inline constexpr const T& min(const T& a, const T& b) |
|||
{ |
|||
return b < a ? b : a; |
|||
} |
|||
|
|||
template <class T, class Compare> |
|||
inline constexpr const T& min(const T& a, const T& b, Compare comp) |
|||
{ |
|||
return comp(b, a) ? b : a; |
|||
} |
|||
|
|||
template <class T> |
|||
inline constexpr const T& max(const T& a, const T& b) |
|||
{ |
|||
return a < b ? b : a; |
|||
} |
|||
|
|||
template <class T, class Compare> |
|||
inline constexpr const T& max(const T& a, const T& b, Compare comp) |
|||
{ |
|||
return comp(a, b) ? b : a; |
|||
} |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,129 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
numeric_digits.hpp |
|||
Construction of numeric digits |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_NUMERIC_DIGITS_HPP |
|||
#define FFUZZYPP_UTILS_NUMERIC_DIGITS_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include <limits> |
|||
|
|||
#include "safe_int.hpp" |
|||
#include "static_assert_query.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace numeric_digits { |
|||
|
|||
template <typename T, size_t Base> |
|||
static constexpr size_t in_base(T value) noexcept |
|||
{ |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<T>(), |
|||
"T must be an unsigned integral type."); |
|||
static_assert(Base >= 2, "Base must be at least two."); |
|||
return (value < Base) ? 1 : 1 + in_base<T, Base>(value / Base); |
|||
} |
|||
|
|||
template <typename T> |
|||
static constexpr size_t in_decimal(T value) noexcept |
|||
{ |
|||
return in_base<T, 10>(value); |
|||
} |
|||
|
|||
// Sanity checks
|
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
namespace internal |
|||
{ |
|||
static constexpr uintmax_t expull(uintmax_t base, uintmax_t exponent) noexcept |
|||
{ |
|||
return |
|||
exponent == 0 ? 1 : |
|||
base * expull(base, exponent-1); |
|||
} |
|||
static_assert(expull(10, 0) == 1ull, "sanity check for expull(10, 0) failed."); |
|||
static_assert(expull(10, 1) == 10ull, "sanity check for expull(10, 1) failed."); |
|||
static_assert(expull(10, 2) == 100ull, "sanity check for expull(10, 2) failed."); |
|||
static_assert(expull(10, 3) == 1000ull, "sanity check for expull(10, 3) failed."); |
|||
static_assert(expull(10, 4) == 10000ull, "sanity check for expull(10, 4) failed."); |
|||
static_assert(expull(10, 19) == 10000000000000000000ull, "sanity check for expull(10, 19) failed."); |
|||
|
|||
template <typename T> |
|||
struct digits_in_decimal_check |
|||
{ |
|||
private: |
|||
template <uintmax_t i> |
|||
struct case_digit1 |
|||
{ |
|||
static constexpr const bool value = in_decimal<T>(i) == 1; |
|||
}; |
|||
template <uintmax_t i> |
|||
struct case_digit2 |
|||
{ |
|||
static constexpr const bool value = in_decimal<T>(i + 10u) == 2; |
|||
}; |
|||
template <uintmax_t i> |
|||
struct case_many_borders |
|||
{ |
|||
static constexpr const bool value0 = |
|||
std::numeric_limits<T>::max() < (expull(10, i+1) - 1) |
|||
|| in_decimal<T>(T(expull(10, i+1) - 1)) == i + 1; |
|||
static constexpr const bool value1 = |
|||
std::numeric_limits<T>::max() < expull(10, i+1) |
|||
|| in_decimal<T>(T(expull(10, i+1))) == i + 2; |
|||
static constexpr const bool value = value0 && value1; |
|||
}; |
|||
public: |
|||
static constexpr const bool value = |
|||
static_assert_query::is_all<case_digit1, 10>::value && |
|||
static_assert_query::is_all<case_digit2, 90>::value && |
|||
static_assert_query::is_all<case_many_borders, 19>::value; |
|||
}; |
|||
#ifdef FFUZZYPP_LOCAL_CHK |
|||
#error do not define FFUZZYPP_LOCAL_CHK |
|||
#endif |
|||
#define FFUZZYPP_LOCAL_CHK(typ) \ |
|||
static_assert(digits_in_decimal_check<typ>::value, \ |
|||
"sanity checks for digits_in_decimal failed for " #typ ".") |
|||
FFUZZYPP_LOCAL_CHK(unsigned char); |
|||
FFUZZYPP_LOCAL_CHK(unsigned short); |
|||
FFUZZYPP_LOCAL_CHK(unsigned int); |
|||
FFUZZYPP_LOCAL_CHK(unsigned long); |
|||
FFUZZYPP_LOCAL_CHK(unsigned long long); |
|||
FFUZZYPP_LOCAL_CHK(uint_least8_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_least16_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_least32_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_least64_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast8_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast16_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast32_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast64_t); |
|||
FFUZZYPP_LOCAL_CHK(uintmax_t); |
|||
FFUZZYPP_LOCAL_CHK(size_t); |
|||
#undef FFUZZYPP_LOCAL_CHK |
|||
} |
|||
#endif |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,116 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
ranges.hpp |
|||
Value range library |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_RANGES_HPP |
|||
#define FFUZZYPP_UTILS_RANGES_HPP |
|||
|
|||
#include <limits> |
|||
|
|||
|
|||
namespace ffuzzy { |
|||
namespace ranges { |
|||
|
|||
/*
|
|||
Inclusive range ([a, b] where a <= b) |
|||
*/ |
|||
template <typename T> |
|||
class inclusive |
|||
{ |
|||
T v_begin; |
|||
T v_end; |
|||
public: |
|||
class iterator |
|||
{ |
|||
private: |
|||
T value; |
|||
T value_end; |
|||
bool is_end; |
|||
private: |
|||
constexpr iterator( |
|||
T value, |
|||
T value_end, |
|||
bool is_end = false |
|||
) noexcept |
|||
: value(value) |
|||
, value_end(value_end) |
|||
, is_end(is_end) |
|||
{ } |
|||
friend class inclusive; |
|||
public: |
|||
constexpr iterator(const iterator&) noexcept = default; |
|||
constexpr T operator*(void) const noexcept |
|||
{ |
|||
return value; |
|||
} |
|||
/* can be constexpr in C++14 */ |
|||
#if __cpp_constexpr >= 201304L |
|||
constexpr |
|||
#endif |
|||
iterator& operator++(void) noexcept |
|||
{ |
|||
if (!is_end) |
|||
{ |
|||
if (value == value_end) |
|||
is_end = true; |
|||
else |
|||
++value; |
|||
} |
|||
return *this; |
|||
} |
|||
public: |
|||
constexpr bool operator==( |
|||
const iterator& other |
|||
) const noexcept |
|||
{ |
|||
return |
|||
value == other.value && |
|||
value_end == other.value_end && |
|||
is_end == other.is_end; |
|||
} |
|||
constexpr bool operator!=( |
|||
const iterator& other |
|||
) const noexcept |
|||
{ |
|||
return !(*this == other); |
|||
} |
|||
}; |
|||
public: |
|||
constexpr iterator begin(void) const noexcept |
|||
{ |
|||
return iterator(v_begin, v_end, false); |
|||
} |
|||
constexpr iterator end(void) const noexcept |
|||
{ |
|||
return iterator(v_end, v_end, true); |
|||
} |
|||
public: |
|||
constexpr inclusive(T vbegin, T vend) noexcept |
|||
: v_begin(vbegin), v_end(vend) { } |
|||
constexpr inclusive(void) noexcept |
|||
: v_begin(std::numeric_limits<T>::min()) |
|||
, v_end (std::numeric_limits<T>::max()) { } |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,371 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
safe_int.hpp |
|||
Safe integer handling utilities |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_SAFE_INT_HPP |
|||
#define FFUZZYPP_UTILS_SAFE_INT_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
#include <limits> |
|||
#include <type_traits> |
|||
|
|||
namespace ffuzzy { |
|||
namespace safe_int { |
|||
|
|||
|
|||
namespace contracts |
|||
{ |
|||
template <typename T> |
|||
static inline constexpr bool is_integral_type(void) noexcept |
|||
{ |
|||
return std::is_integral<T>::value; |
|||
} |
|||
template <typename T> |
|||
static inline constexpr bool is_signed_integral_type(void) noexcept |
|||
{ |
|||
return is_integral_type<T>() && |
|||
std::is_unsigned<T>::value == false && |
|||
std::numeric_limits<T>::max() > 0 && |
|||
std::numeric_limits<T>::min() < 0; |
|||
} |
|||
template <typename T> |
|||
static inline constexpr bool is_unsigned_integral_type(void) noexcept |
|||
{ |
|||
return is_integral_type<T>() && |
|||
std::is_unsigned<T>::value == true && |
|||
std::numeric_limits<T>::min() == 0; |
|||
} |
|||
template <typename T> |
|||
static inline constexpr bool is_binary_integral_type(void) noexcept |
|||
{ |
|||
return |
|||
std::numeric_limits<T>::radix == 2 && |
|||
std::numeric_limits<T>::digits >= 0; |
|||
} |
|||
|
|||
// Same implementation but copying for better diagnostics
|
|||
template <typename T> |
|||
struct integral_type |
|||
{ |
|||
static_assert(std::is_integral<T>::value, |
|||
"T must be an integral type."); |
|||
}; |
|||
template <typename T> |
|||
struct signed_integral_type |
|||
: integral_type<T> |
|||
{ |
|||
static_assert(std::is_unsigned<T>::value == false, |
|||
"T must be a signed integral type."); |
|||
static_assert(std::numeric_limits<T>::max() > 0, |
|||
"numeric_limits<T>::max() must be positive for signed integral type."); |
|||
static_assert(std::numeric_limits<T>::min() < 0, |
|||
"numeric_limits<T>::min() must be negative for signed integral type."); |
|||
}; |
|||
template <typename T> |
|||
struct unsigned_integral_type |
|||
: integral_type<T> |
|||
{ |
|||
static_assert(std::is_unsigned<T>::value == true, |
|||
"T must be an unsigned integral type."); |
|||
static_assert(std::numeric_limits<T>::min() == 0, |
|||
"numeric_limits<T>::min() must be zero for unsigned integral type."); |
|||
}; |
|||
template <typename T> |
|||
struct binary_integral_type |
|||
: integral_type<T> |
|||
{ |
|||
static_assert(std::numeric_limits<T>::radix == 2, |
|||
"numeric_limits<T>::radix must be 2 for binary integral type."); |
|||
static_assert(std::numeric_limits<T>::digits >= 0, |
|||
"numeric_limits<T>::digits must not be negative for binary integral type."); |
|||
}; |
|||
} |
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename T1, typename T2> |
|||
struct common_unsigned_integral_type |
|||
: contracts::unsigned_integral_type<T1> |
|||
, contracts::unsigned_integral_type<T2> |
|||
{ |
|||
typedef typename std::conditional< |
|||
std::numeric_limits<T1>::max() < std::numeric_limits<T2>::max(), |
|||
T2, T1 |
|||
>::type type; |
|||
}; |
|||
template <typename T> |
|||
struct common_unsigned_integral_type<T, T> |
|||
{ |
|||
typedef T type; |
|||
}; |
|||
|
|||
template <typename Tcommon, typename Tvalue, Tcommon Value, bool IsValid = true> |
|||
struct value_base |
|||
{ |
|||
typedef Tcommon common_type; |
|||
typedef Tvalue value_type; |
|||
static constexpr const bool is_valid = IsValid |
|||
&& std::numeric_limits<Tvalue>::min() <= Value |
|||
&& std::numeric_limits<Tvalue>::max() >= Value; |
|||
static constexpr const Tcommon value_in_common = Value; |
|||
static constexpr const Tvalue value = |
|||
is_valid ? static_cast<Tvalue>(Value) : 0; |
|||
}; |
|||
} |
|||
|
|||
|
|||
template <typename T, uintmax_t Value, bool IsValid = true> |
|||
struct uvalue |
|||
: contracts::unsigned_integral_type<T> |
|||
, public internal::value_base<uintmax_t, T, Value, IsValid> |
|||
{}; |
|||
|
|||
template <typename T, intmax_t Value, bool IsValid = true> |
|||
struct svalue |
|||
: contracts::signed_integral_type<T> |
|||
, public internal::value_base<intmax_t, T, Value, IsValid> |
|||
{}; |
|||
|
|||
|
|||
|
|||
|
|||
namespace operations |
|||
{ |
|||
|
|||
template <typename TV1, typename TV2> struct safe_add; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_add<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V1 + V2, B1 && B2 && ( |
|||
std::numeric_limits<common_type>::max() - V1 >= V2 |
|||
)> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TV1, typename TV2> struct safe_sub; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_sub<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V1 - V2, B1 && B2 && V1 >= V2> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TV1, typename TV2> struct safe_mul; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_mul<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V1 * V2, B1 && B2 && ( |
|||
V1 == 0 || V2 == 0 || |
|||
std::numeric_limits<common_type>::max() / V2 > V1 |
|||
)> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TV1, typename TV2> struct safe_div; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_div<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V2 ? V1 / V2 : 0, B1 && B2 && V2 != 0> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TVbase, typename TVshift> struct safe_lshift; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_lshift<uvalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
// Refer C++11 specification [3.9.1 p3] for reason that
|
|||
// conversion to "unsigned" for non-negative "int" numbers is safe.
|
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits) |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_lshift<uvalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_lshift<svalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits) |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_lshift<svalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TVbase, typename TVshift> struct safe_rshift; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_rshift<uvalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits); |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_rshift<uvalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits; |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_rshift<svalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits); |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_rshift<svalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits; |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
// internal::value_base performs some required tests
|
|||
template <typename Tnew, typename TV> struct safe_cast_to_u; |
|||
template <typename Tnew, typename Told, uintmax_t V, bool B> |
|||
struct safe_cast_to_u<Tnew, uvalue<Told, V, B>> |
|||
{ |
|||
typedef uvalue<Tnew, V, B> type; |
|||
}; |
|||
template <typename Tnew, typename Told, intmax_t V, bool B> |
|||
struct safe_cast_to_u<Tnew, svalue<Told, V, B>> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = B && V >= 0; |
|||
public: |
|||
typedef uvalue<Tnew, |
|||
is_valid ? static_cast<uintmax_t>(V) : 0, |
|||
is_valid> type; |
|||
}; |
|||
template <typename Tnew, typename TV> struct safe_cast_to_s; |
|||
template <typename Tnew, typename Told, intmax_t V, bool B> |
|||
struct safe_cast_to_s<Tnew, svalue<Told, V, B>> |
|||
{ |
|||
typedef svalue<Tnew, V, B> type; |
|||
}; |
|||
template <typename Tnew, typename Told, uintmax_t V, bool B> |
|||
struct safe_cast_to_s<Tnew, uvalue<Told, V, B>> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = B |
|||
&& V <= static_cast<uintmax_t>(std::numeric_limits<intmax_t>::max()); |
|||
public: |
|||
typedef svalue<Tnew, |
|||
is_valid ? static_cast<intmax_t>(V) : 0, |
|||
is_valid> type; |
|||
}; |
|||
} |
|||
template <typename Tnew, typename TV> |
|||
struct safe_cast |
|||
: public std::conditional< |
|||
contracts::is_unsigned_integral_type<Tnew>(), |
|||
internal::safe_cast_to_u<Tnew, TV>, |
|||
typename std::conditional< |
|||
contracts::is_signed_integral_type<Tnew>(), |
|||
internal::safe_cast_to_s<Tnew, TV>, void |
|||
>::type |
|||
>::type {}; |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
template <typename TV1, typename TV2> |
|||
using safe_add = typename operations::safe_add<TV1, TV2>::type; |
|||
template <typename TV1, typename TV2> |
|||
using safe_sub = typename operations::safe_sub<TV1, TV2>::type; |
|||
template <typename TV1, typename TV2> |
|||
using safe_mul = typename operations::safe_mul<TV1, TV2>::type; |
|||
template <typename TV1, typename TV2> |
|||
using safe_div = typename operations::safe_div<TV1, TV2>::type; |
|||
template <typename TVbase, typename TVshift> |
|||
using safe_lshift = typename operations::safe_lshift<TVbase, TVshift>::type; |
|||
template <typename TVbase, typename TVshift> |
|||
using safe_rshift = typename operations::safe_rshift<TVbase, TVshift>::type; |
|||
template <typename Tnew, typename TV> |
|||
using safe_cast = typename operations::safe_cast<Tnew, TV>::type; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,97 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
static_assert_query.hpp |
|||
Basic queries for static assertions |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_STATIC_ASSERT_QUERY_HPP |
|||
#define FFUZZYPP_UTILS_STATIC_ASSERT_QUERY_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
namespace ffuzzy { |
|||
namespace static_assert_query { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <template <uintmax_t> class T, uintmax_t a, uintmax_t b> |
|||
struct is_all_bounds |
|||
{ |
|||
static_assert(a <= b, "bounds must be valid (a must not exceed b)."); |
|||
/*
|
|||
For unsigned types, |
|||
a/2 + b/2 + ((a%2)+(b%2))/2 |
|||
is a remplacement of |
|||
(a+b)/2 |
|||
while preventing arithmetic overflow of (a+b). |
|||
*/ |
|||
static constexpr const bool value = |
|||
a > b ? false : |
|||
is_all_bounds<T, a, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2>::value && |
|||
is_all_bounds<T, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2 + 1, b>::value; |
|||
}; |
|||
template <template <uintmax_t> class T, uintmax_t a> |
|||
struct is_all_bounds<T, a, a> |
|||
{ |
|||
static constexpr const bool value = T<a>::value; |
|||
}; |
|||
|
|||
template <template <uintmax_t> class T, uintmax_t a, uintmax_t b> |
|||
struct is_any_bounds |
|||
{ |
|||
static_assert(a <= b, "bounds must be valid (a must not exceed b)."); |
|||
static constexpr const bool value = |
|||
a > b ? false : |
|||
is_any_bounds<T, a, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2>::value || |
|||
is_any_bounds<T, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2 + 1, b>::value; |
|||
}; |
|||
template <template <uintmax_t> class T, uintmax_t a> |
|||
struct is_any_bounds<T, a, a> |
|||
{ |
|||
static constexpr const bool value = T<a>::value; |
|||
}; |
|||
} |
|||
|
|||
|
|||
template <template <uintmax_t> class T, uintmax_t n> |
|||
struct is_all |
|||
{ |
|||
static constexpr const bool value = internal::is_all_bounds<T, 0, n-1>::value; |
|||
}; |
|||
template <template <uintmax_t> class T> |
|||
struct is_all<T, 0> |
|||
{ |
|||
static constexpr const bool value = true; |
|||
}; |
|||
|
|||
template <template <uintmax_t> class T, uintmax_t n> |
|||
struct is_any |
|||
{ |
|||
static constexpr const bool value = internal::is_any_bounds<T, 0, n-1>::value; |
|||
}; |
|||
template <template <uintmax_t> class T> |
|||
struct is_any<T, 0> |
|||
{ |
|||
static constexpr const bool value = false; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,67 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
type_modifier.hpp |
|||
C++11 type modifier utilities |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_TYPE_MODIFIER_HPP |
|||
#define FFUZZYPP_UTILS_TYPE_MODIFIER_HPP |
|||
|
|||
#include <type_traits> |
|||
|
|||
namespace ffuzzy { |
|||
namespace type_mod { |
|||
|
|||
template <typename T, bool IsConst, bool IsVolatile> |
|||
struct cv_selector; |
|||
template <typename T> |
|||
struct cv_selector<T, false, false> |
|||
{ |
|||
typedef T type; |
|||
}; |
|||
template <typename T> |
|||
struct cv_selector<T, true, false> |
|||
{ |
|||
typedef const T type; |
|||
}; |
|||
template <typename T> |
|||
struct cv_selector<T, false, true> |
|||
{ |
|||
typedef volatile T type; |
|||
}; |
|||
template <typename T> |
|||
struct cv_selector<T, true, true> |
|||
{ |
|||
typedef const volatile T type; |
|||
}; |
|||
template < |
|||
typename T, |
|||
typename Tmatch, |
|||
bool IsConst = std::is_const<T>::value, |
|||
bool IsVolatile = std::is_volatile<T>::value |
|||
> |
|||
struct cv_match |
|||
{ |
|||
typedef typename cv_selector<Tmatch, IsConst, IsVolatile>::type type; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,2 @@ |
|||
/COPYING* -whitespace |
|||
*.md -whitespace |
@ -0,0 +1,41 @@ |
|||
.* |
|||
!.git?* |
|||
*~ |
|||
*.a |
|||
*.diff |
|||
*.dll |
|||
*.exe |
|||
*.in |
|||
*.la |
|||
*.lo |
|||
*.o |
|||
*.obj |
|||
*.out |
|||
*.patch |
|||
*.pdb |
|||
*.swp |
|||
*.tmp |
|||
aclocal.m4 |
|||
autoscan.log |
|||
autom4te.cache |
|||
config.log |
|||
config.status |
|||
configure |
|||
configure.scan |
|||
confdefs* |
|||
conftest* |
|||
conf[0-9]* |
|||
ffuzzy_config.h |
|||
libtool |
|||
Makefile |
|||
so_locations |
|||
stamp-h1 |
|||
tmp* |
|||
_libs |
|||
|
|||
ffuzzypp-* |
|||
|
|||
/Doxyfile |
|||
/html/ |
|||
/latex/ |
|||
/doc/ |
@ -0,0 +1,23 @@ |
|||
Boost Software License - Version 1.0 - August 17th, 2003 |
|||
|
|||
Permission is hereby granted, free of charge, to any person or organization |
|||
obtaining a copy of the software and accompanying documentation covered by |
|||
this license (the "Software") to use, reproduce, display, distribute, |
|||
execute, and transmit the Software, and to prepare derivative works of the |
|||
Software, and to permit third-parties to whom the Software is furnished to |
|||
do so, all subject to the following: |
|||
|
|||
The copyright notices in the Software and this entire statement, including |
|||
the above license grant, this restriction and the following disclaimer, |
|||
must be included in all copies of the Software, in whole or in part, and |
|||
all derivative works of the Software, unless such copies or derivative |
|||
works are solely in the form of machine-executable object code generated by |
|||
a source language processor. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
|||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
|||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
|||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|||
DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,339 @@ |
|||
GNU GENERAL PUBLIC LICENSE |
|||
Version 2, June 1991 |
|||
|
|||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|||
Everyone is permitted to copy and distribute verbatim copies |
|||
of this license document, but changing it is not allowed. |
|||
|
|||
Preamble |
|||
|
|||
The licenses for most software are designed to take away your |
|||
freedom to share and change it. By contrast, the GNU General Public |
|||
License is intended to guarantee your freedom to share and change free |
|||
software--to make sure the software is free for all its users. This |
|||
General Public License applies to most of the Free Software |
|||
Foundation's software and to any other program whose authors commit to |
|||
using it. (Some other Free Software Foundation software is covered by |
|||
the GNU Lesser General Public License instead.) You can apply it to |
|||
your programs, too. |
|||
|
|||
When we speak of free software, we are referring to freedom, not |
|||
price. Our General Public Licenses are designed to make sure that you |
|||
have the freedom to distribute copies of free software (and charge for |
|||
this service if you wish), that you receive source code or can get it |
|||
if you want it, that you can change the software or use pieces of it |
|||
in new free programs; and that you know you can do these things. |
|||
|
|||
To protect your rights, we need to make restrictions that forbid |
|||
anyone to deny you these rights or to ask you to surrender the rights. |
|||
These restrictions translate to certain responsibilities for you if you |
|||
distribute copies of the software, or if you modify it. |
|||
|
|||
For example, if you distribute copies of such a program, whether |
|||
gratis or for a fee, you must give the recipients all the rights that |
|||
you have. You must make sure that they, too, receive or can get the |
|||
source code. And you must show them these terms so they know their |
|||
rights. |
|||
|
|||
We protect your rights with two steps: (1) copyright the software, and |
|||
(2) offer you this license which gives you legal permission to copy, |
|||
distribute and/or modify the software. |
|||
|
|||
Also, for each author's protection and ours, we want to make certain |
|||
that everyone understands that there is no warranty for this free |
|||
software. If the software is modified by someone else and passed on, we |
|||
want its recipients to know that what they have is not the original, so |
|||
that any problems introduced by others will not reflect on the original |
|||
authors' reputations. |
|||
|
|||
Finally, any free program is threatened constantly by software |
|||
patents. We wish to avoid the danger that redistributors of a free |
|||
program will individually obtain patent licenses, in effect making the |
|||
program proprietary. To prevent this, we have made it clear that any |
|||
patent must be licensed for everyone's free use or not licensed at all. |
|||
|
|||
The precise terms and conditions for copying, distribution and |
|||
modification follow. |
|||
|
|||
GNU GENERAL PUBLIC LICENSE |
|||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
|||
|
|||
0. This License applies to any program or other work which contains |
|||
a notice placed by the copyright holder saying it may be distributed |
|||
under the terms of this General Public License. The "Program", below, |
|||
refers to any such program or work, and a "work based on the Program" |
|||
means either the Program or any derivative work under copyright law: |
|||
that is to say, a work containing the Program or a portion of it, |
|||
either verbatim or with modifications and/or translated into another |
|||
language. (Hereinafter, translation is included without limitation in |
|||
the term "modification".) Each licensee is addressed as "you". |
|||
|
|||
Activities other than copying, distribution and modification are not |
|||
covered by this License; they are outside its scope. The act of |
|||
running the Program is not restricted, and the output from the Program |
|||
is covered only if its contents constitute a work based on the |
|||
Program (independent of having been made by running the Program). |
|||
Whether that is true depends on what the Program does. |
|||
|
|||
1. You may copy and distribute verbatim copies of the Program's |
|||
source code as you receive it, in any medium, provided that you |
|||
conspicuously and appropriately publish on each copy an appropriate |
|||
copyright notice and disclaimer of warranty; keep intact all the |
|||
notices that refer to this License and to the absence of any warranty; |
|||
and give any other recipients of the Program a copy of this License |
|||
along with the Program. |
|||
|
|||
You may charge a fee for the physical act of transferring a copy, and |
|||
you may at your option offer warranty protection in exchange for a fee. |
|||
|
|||
2. You may modify your copy or copies of the Program or any portion |
|||
of it, thus forming a work based on the Program, and copy and |
|||
distribute such modifications or work under the terms of Section 1 |
|||
above, provided that you also meet all of these conditions: |
|||
|
|||
a) You must cause the modified files to carry prominent notices |
|||
stating that you changed the files and the date of any change. |
|||
|
|||
b) You must cause any work that you distribute or publish, that in |
|||
whole or in part contains or is derived from the Program or any |
|||
part thereof, to be licensed as a whole at no charge to all third |
|||
parties under the terms of this License. |
|||
|
|||
c) If the modified program normally reads commands interactively |
|||
when run, you must cause it, when started running for such |
|||
interactive use in the most ordinary way, to print or display an |
|||
announcement including an appropriate copyright notice and a |
|||
notice that there is no warranty (or else, saying that you provide |
|||
a warranty) and that users may redistribute the program under |
|||
these conditions, and telling the user how to view a copy of this |
|||
License. (Exception: if the Program itself is interactive but |
|||
does not normally print such an announcement, your work based on |
|||
the Program is not required to print an announcement.) |
|||
|
|||
These requirements apply to the modified work as a whole. If |
|||
identifiable sections of that work are not derived from the Program, |
|||
and can be reasonably considered independent and separate works in |
|||
themselves, then this License, and its terms, do not apply to those |
|||
sections when you distribute them as separate works. But when you |
|||
distribute the same sections as part of a whole which is a work based |
|||
on the Program, the distribution of the whole must be on the terms of |
|||
this License, whose permissions for other licensees extend to the |
|||
entire whole, and thus to each and every part regardless of who wrote it. |
|||
|
|||
Thus, it is not the intent of this section to claim rights or contest |
|||
your rights to work written entirely by you; rather, the intent is to |
|||
exercise the right to control the distribution of derivative or |
|||
collective works based on the Program. |
|||
|
|||
In addition, mere aggregation of another work not based on the Program |
|||
with the Program (or with a work based on the Program) on a volume of |
|||
a storage or distribution medium does not bring the other work under |
|||
the scope of this License. |
|||
|
|||
3. You may copy and distribute the Program (or a work based on it, |
|||
under Section 2) in object code or executable form under the terms of |
|||
Sections 1 and 2 above provided that you also do one of the following: |
|||
|
|||
a) Accompany it with the complete corresponding machine-readable |
|||
source code, which must be distributed under the terms of Sections |
|||
1 and 2 above on a medium customarily used for software interchange; or, |
|||
|
|||
b) Accompany it with a written offer, valid for at least three |
|||
years, to give any third party, for a charge no more than your |
|||
cost of physically performing source distribution, a complete |
|||
machine-readable copy of the corresponding source code, to be |
|||
distributed under the terms of Sections 1 and 2 above on a medium |
|||
customarily used for software interchange; or, |
|||
|
|||
c) Accompany it with the information you received as to the offer |
|||
to distribute corresponding source code. (This alternative is |
|||
allowed only for noncommercial distribution and only if you |
|||
received the program in object code or executable form with such |
|||
an offer, in accord with Subsection b above.) |
|||
|
|||
The source code for a work means the preferred form of the work for |
|||
making modifications to it. For an executable work, complete source |
|||
code means all the source code for all modules it contains, plus any |
|||
associated interface definition files, plus the scripts used to |
|||
control compilation and installation of the executable. However, as a |
|||
special exception, the source code distributed need not include |
|||
anything that is normally distributed (in either source or binary |
|||
form) with the major components (compiler, kernel, and so on) of the |
|||
operating system on which the executable runs, unless that component |
|||
itself accompanies the executable. |
|||
|
|||
If distribution of executable or object code is made by offering |
|||
access to copy from a designated place, then offering equivalent |
|||
access to copy the source code from the same place counts as |
|||
distribution of the source code, even though third parties are not |
|||
compelled to copy the source along with the object code. |
|||
|
|||
4. You may not copy, modify, sublicense, or distribute the Program |
|||
except as expressly provided under this License. Any attempt |
|||
otherwise to copy, modify, sublicense or distribute the Program is |
|||
void, and will automatically terminate your rights under this License. |
|||
However, parties who have received copies, or rights, from you under |
|||
this License will not have their licenses terminated so long as such |
|||
parties remain in full compliance. |
|||
|
|||
5. You are not required to accept this License, since you have not |
|||
signed it. However, nothing else grants you permission to modify or |
|||
distribute the Program or its derivative works. These actions are |
|||
prohibited by law if you do not accept this License. Therefore, by |
|||
modifying or distributing the Program (or any work based on the |
|||
Program), you indicate your acceptance of this License to do so, and |
|||
all its terms and conditions for copying, distributing or modifying |
|||
the Program or works based on it. |
|||
|
|||
6. Each time you redistribute the Program (or any work based on the |
|||
Program), the recipient automatically receives a license from the |
|||
original licensor to copy, distribute or modify the Program subject to |
|||
these terms and conditions. You may not impose any further |
|||
restrictions on the recipients' exercise of the rights granted herein. |
|||
You are not responsible for enforcing compliance by third parties to |
|||
this License. |
|||
|
|||
7. If, as a consequence of a court judgment or allegation of patent |
|||
infringement or for any other reason (not limited to patent issues), |
|||
conditions are imposed on you (whether by court order, agreement or |
|||
otherwise) that contradict the conditions of this License, they do not |
|||
excuse you from the conditions of this License. If you cannot |
|||
distribute so as to satisfy simultaneously your obligations under this |
|||
License and any other pertinent obligations, then as a consequence you |
|||
may not distribute the Program at all. For example, if a patent |
|||
license would not permit royalty-free redistribution of the Program by |
|||
all those who receive copies directly or indirectly through you, then |
|||
the only way you could satisfy both it and this License would be to |
|||
refrain entirely from distribution of the Program. |
|||
|
|||
If any portion of this section is held invalid or unenforceable under |
|||
any particular circumstance, the balance of the section is intended to |
|||
apply and the section as a whole is intended to apply in other |
|||
circumstances. |
|||
|
|||
It is not the purpose of this section to induce you to infringe any |
|||
patents or other property right claims or to contest validity of any |
|||
such claims; this section has the sole purpose of protecting the |
|||
integrity of the free software distribution system, which is |
|||
implemented by public license practices. Many people have made |
|||
generous contributions to the wide range of software distributed |
|||
through that system in reliance on consistent application of that |
|||
system; it is up to the author/donor to decide if he or she is willing |
|||
to distribute software through any other system and a licensee cannot |
|||
impose that choice. |
|||
|
|||
This section is intended to make thoroughly clear what is believed to |
|||
be a consequence of the rest of this License. |
|||
|
|||
8. If the distribution and/or use of the Program is restricted in |
|||
certain countries either by patents or by copyrighted interfaces, the |
|||
original copyright holder who places the Program under this License |
|||
may add an explicit geographical distribution limitation excluding |
|||
those countries, so that distribution is permitted only in or among |
|||
countries not thus excluded. In such case, this License incorporates |
|||
the limitation as if written in the body of this License. |
|||
|
|||
9. The Free Software Foundation may publish revised and/or new versions |
|||
of the General Public License from time to time. Such new versions will |
|||
be similar in spirit to the present version, but may differ in detail to |
|||
address new problems or concerns. |
|||
|
|||
Each version is given a distinguishing version number. If the Program |
|||
specifies a version number of this License which applies to it and "any |
|||
later version", you have the option of following the terms and conditions |
|||
either of that version or of any later version published by the Free |
|||
Software Foundation. If the Program does not specify a version number of |
|||
this License, you may choose any version ever published by the Free Software |
|||
Foundation. |
|||
|
|||
10. If you wish to incorporate parts of the Program into other free |
|||
programs whose distribution conditions are different, write to the author |
|||
to ask for permission. For software which is copyrighted by the Free |
|||
Software Foundation, write to the Free Software Foundation; we sometimes |
|||
make exceptions for this. Our decision will be guided by the two goals |
|||
of preserving the free status of all derivatives of our free software and |
|||
of promoting the sharing and reuse of software generally. |
|||
|
|||
NO WARRANTY |
|||
|
|||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
|||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
|||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
|||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED |
|||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS |
|||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE |
|||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, |
|||
REPAIR OR CORRECTION. |
|||
|
|||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
|||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, |
|||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
|||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED |
|||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY |
|||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
|||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
|||
POSSIBILITY OF SUCH DAMAGES. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
How to Apply These Terms to Your New Programs |
|||
|
|||
If you develop a new program, and you want it to be of the greatest |
|||
possible use to the public, the best way to achieve this is to make it |
|||
free software which everyone can redistribute and change under these terms. |
|||
|
|||
To do so, attach the following notices to the program. It is safest |
|||
to attach them to the start of each source file to most effectively |
|||
convey the exclusion of warranty; and each file should have at least |
|||
the "copyright" line and a pointer to where the full notice is found. |
|||
|
|||
<one line to give the program's name and a brief idea of what it does.> |
|||
Copyright (C) <year> <name of author> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
|
|||
Also add information on how to contact you by electronic and paper mail. |
|||
|
|||
If the program is interactive, make it output a short notice like this |
|||
when it starts in an interactive mode: |
|||
|
|||
Gnomovision version 69, Copyright (C) year name of author |
|||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
|||
This is free software, and you are welcome to redistribute it |
|||
under certain conditions; type `show c' for details. |
|||
|
|||
The hypothetical commands `show w' and `show c' should show the appropriate |
|||
parts of the General Public License. Of course, the commands you use may |
|||
be called something other than `show w' and `show c'; they could even be |
|||
mouse-clicks or menu items--whatever suits your program. |
|||
|
|||
You should also get your employer (if you work as a programmer) or your |
|||
school, if any, to sign a "copyright disclaimer" for the program, if |
|||
necessary. Here is a sample; alter the names: |
|||
|
|||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program |
|||
`Gnomovision' (which makes passes at compilers) written by James Hacker. |
|||
|
|||
<signature of Ty Coon>, 1 April 1989 |
|||
Ty Coon, President of Vice |
|||
|
|||
This General Public License does not permit incorporating your program into |
|||
proprietary programs. If your program is a subroutine library, you may |
|||
consider it more useful to permit linking proprietary applications with the |
|||
library. If this is what you want to do, use the GNU Lesser General |
|||
Public License instead of this License. |
@ -0,0 +1,674 @@ |
|||
GNU GENERAL PUBLIC LICENSE |
|||
Version 3, 29 June 2007 |
|||
|
|||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
|||
Everyone is permitted to copy and distribute verbatim copies |
|||
of this license document, but changing it is not allowed. |
|||
|
|||
Preamble |
|||
|
|||
The GNU General Public License is a free, copyleft license for |
|||
software and other kinds of works. |
|||
|
|||
The licenses for most software and other practical works are designed |
|||
to take away your freedom to share and change the works. By contrast, |
|||
the GNU General Public License is intended to guarantee your freedom to |
|||
share and change all versions of a program--to make sure it remains free |
|||
software for all its users. We, the Free Software Foundation, use the |
|||
GNU General Public License for most of our software; it applies also to |
|||
any other work released this way by its authors. You can apply it to |
|||
your programs, too. |
|||
|
|||
When we speak of free software, we are referring to freedom, not |
|||
price. Our General Public Licenses are designed to make sure that you |
|||
have the freedom to distribute copies of free software (and charge for |
|||
them if you wish), that you receive source code or can get it if you |
|||
want it, that you can change the software or use pieces of it in new |
|||
free programs, and that you know you can do these things. |
|||
|
|||
To protect your rights, we need to prevent others from denying you |
|||
these rights or asking you to surrender the rights. Therefore, you have |
|||
certain responsibilities if you distribute copies of the software, or if |
|||
you modify it: responsibilities to respect the freedom of others. |
|||
|
|||
For example, if you distribute copies of such a program, whether |
|||
gratis or for a fee, you must pass on to the recipients the same |
|||
freedoms that you received. You must make sure that they, too, receive |
|||
or can get the source code. And you must show them these terms so they |
|||
know their rights. |
|||
|
|||
Developers that use the GNU GPL protect your rights with two steps: |
|||
(1) assert copyright on the software, and (2) offer you this License |
|||
giving you legal permission to copy, distribute and/or modify it. |
|||
|
|||
For the developers' and authors' protection, the GPL clearly explains |
|||
that there is no warranty for this free software. For both users' and |
|||
authors' sake, the GPL requires that modified versions be marked as |
|||
changed, so that their problems will not be attributed erroneously to |
|||
authors of previous versions. |
|||
|
|||
Some devices are designed to deny users access to install or run |
|||
modified versions of the software inside them, although the manufacturer |
|||
can do so. This is fundamentally incompatible with the aim of |
|||
protecting users' freedom to change the software. The systematic |
|||
pattern of such abuse occurs in the area of products for individuals to |
|||
use, which is precisely where it is most unacceptable. Therefore, we |
|||
have designed this version of the GPL to prohibit the practice for those |
|||
products. If such problems arise substantially in other domains, we |
|||
stand ready to extend this provision to those domains in future versions |
|||
of the GPL, as needed to protect the freedom of users. |
|||
|
|||
Finally, every program is threatened constantly by software patents. |
|||
States should not allow patents to restrict development and use of |
|||
software on general-purpose computers, but in those that do, we wish to |
|||
avoid the special danger that patents applied to a free program could |
|||
make it effectively proprietary. To prevent this, the GPL assures that |
|||
patents cannot be used to render the program non-free. |
|||
|
|||
The precise terms and conditions for copying, distribution and |
|||
modification follow. |
|||
|
|||
TERMS AND CONDITIONS |
|||
|
|||
0. Definitions. |
|||
|
|||
"This License" refers to version 3 of the GNU General Public License. |
|||
|
|||
"Copyright" also means copyright-like laws that apply to other kinds of |
|||
works, such as semiconductor masks. |
|||
|
|||
"The Program" refers to any copyrightable work licensed under this |
|||
License. Each licensee is addressed as "you". "Licensees" and |
|||
"recipients" may be individuals or organizations. |
|||
|
|||
To "modify" a work means to copy from or adapt all or part of the work |
|||
in a fashion requiring copyright permission, other than the making of an |
|||
exact copy. The resulting work is called a "modified version" of the |
|||
earlier work or a work "based on" the earlier work. |
|||
|
|||
A "covered work" means either the unmodified Program or a work based |
|||
on the Program. |
|||
|
|||
To "propagate" a work means to do anything with it that, without |
|||
permission, would make you directly or secondarily liable for |
|||
infringement under applicable copyright law, except executing it on a |
|||
computer or modifying a private copy. Propagation includes copying, |
|||
distribution (with or without modification), making available to the |
|||
public, and in some countries other activities as well. |
|||
|
|||
To "convey" a work means any kind of propagation that enables other |
|||
parties to make or receive copies. Mere interaction with a user through |
|||
a computer network, with no transfer of a copy, is not conveying. |
|||
|
|||
An interactive user interface displays "Appropriate Legal Notices" |
|||
to the extent that it includes a convenient and prominently visible |
|||
feature that (1) displays an appropriate copyright notice, and (2) |
|||
tells the user that there is no warranty for the work (except to the |
|||
extent that warranties are provided), that licensees may convey the |
|||
work under this License, and how to view a copy of this License. If |
|||
the interface presents a list of user commands or options, such as a |
|||
menu, a prominent item in the list meets this criterion. |
|||
|
|||
1. Source Code. |
|||
|
|||
The "source code" for a work means the preferred form of the work |
|||
for making modifications to it. "Object code" means any non-source |
|||
form of a work. |
|||
|
|||
A "Standard Interface" means an interface that either is an official |
|||
standard defined by a recognized standards body, or, in the case of |
|||
interfaces specified for a particular programming language, one that |
|||
is widely used among developers working in that language. |
|||
|
|||
The "System Libraries" of an executable work include anything, other |
|||
than the work as a whole, that (a) is included in the normal form of |
|||
packaging a Major Component, but which is not part of that Major |
|||
Component, and (b) serves only to enable use of the work with that |
|||
Major Component, or to implement a Standard Interface for which an |
|||
implementation is available to the public in source code form. A |
|||
"Major Component", in this context, means a major essential component |
|||
(kernel, window system, and so on) of the specific operating system |
|||
(if any) on which the executable work runs, or a compiler used to |
|||
produce the work, or an object code interpreter used to run it. |
|||
|
|||
The "Corresponding Source" for a work in object code form means all |
|||
the source code needed to generate, install, and (for an executable |
|||
work) run the object code and to modify the work, including scripts to |
|||
control those activities. However, it does not include the work's |
|||
System Libraries, or general-purpose tools or generally available free |
|||
programs which are used unmodified in performing those activities but |
|||
which are not part of the work. For example, Corresponding Source |
|||
includes interface definition files associated with source files for |
|||
the work, and the source code for shared libraries and dynamically |
|||
linked subprograms that the work is specifically designed to require, |
|||
such as by intimate data communication or control flow between those |
|||
subprograms and other parts of the work. |
|||
|
|||
The Corresponding Source need not include anything that users |
|||
can regenerate automatically from other parts of the Corresponding |
|||
Source. |
|||
|
|||
The Corresponding Source for a work in source code form is that |
|||
same work. |
|||
|
|||
2. Basic Permissions. |
|||
|
|||
All rights granted under this License are granted for the term of |
|||
copyright on the Program, and are irrevocable provided the stated |
|||
conditions are met. This License explicitly affirms your unlimited |
|||
permission to run the unmodified Program. The output from running a |
|||
covered work is covered by this License only if the output, given its |
|||
content, constitutes a covered work. This License acknowledges your |
|||
rights of fair use or other equivalent, as provided by copyright law. |
|||
|
|||
You may make, run and propagate covered works that you do not |
|||
convey, without conditions so long as your license otherwise remains |
|||
in force. You may convey covered works to others for the sole purpose |
|||
of having them make modifications exclusively for you, or provide you |
|||
with facilities for running those works, provided that you comply with |
|||
the terms of this License in conveying all material for which you do |
|||
not control copyright. Those thus making or running the covered works |
|||
for you must do so exclusively on your behalf, under your direction |
|||
and control, on terms that prohibit them from making any copies of |
|||
your copyrighted material outside their relationship with you. |
|||
|
|||
Conveying under any other circumstances is permitted solely under |
|||
the conditions stated below. Sublicensing is not allowed; section 10 |
|||
makes it unnecessary. |
|||
|
|||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
|||
|
|||
No covered work shall be deemed part of an effective technological |
|||
measure under any applicable law fulfilling obligations under article |
|||
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
|||
similar laws prohibiting or restricting circumvention of such |
|||
measures. |
|||
|
|||
When you convey a covered work, you waive any legal power to forbid |
|||
circumvention of technological measures to the extent such circumvention |
|||
is effected by exercising rights under this License with respect to |
|||
the covered work, and you disclaim any intention to limit operation or |
|||
modification of the work as a means of enforcing, against the work's |
|||
users, your or third parties' legal rights to forbid circumvention of |
|||
technological measures. |
|||
|
|||
4. Conveying Verbatim Copies. |
|||
|
|||
You may convey verbatim copies of the Program's source code as you |
|||
receive it, in any medium, provided that you conspicuously and |
|||
appropriately publish on each copy an appropriate copyright notice; |
|||
keep intact all notices stating that this License and any |
|||
non-permissive terms added in accord with section 7 apply to the code; |
|||
keep intact all notices of the absence of any warranty; and give all |
|||
recipients a copy of this License along with the Program. |
|||
|
|||
You may charge any price or no price for each copy that you convey, |
|||
and you may offer support or warranty protection for a fee. |
|||
|
|||
5. Conveying Modified Source Versions. |
|||
|
|||
You may convey a work based on the Program, or the modifications to |
|||
produce it from the Program, in the form of source code under the |
|||
terms of section 4, provided that you also meet all of these conditions: |
|||
|
|||
a) The work must carry prominent notices stating that you modified |
|||
it, and giving a relevant date. |
|||
|
|||
b) The work must carry prominent notices stating that it is |
|||
released under this License and any conditions added under section |
|||
7. This requirement modifies the requirement in section 4 to |
|||
"keep intact all notices". |
|||
|
|||
c) You must license the entire work, as a whole, under this |
|||
License to anyone who comes into possession of a copy. This |
|||
License will therefore apply, along with any applicable section 7 |
|||
additional terms, to the whole of the work, and all its parts, |
|||
regardless of how they are packaged. This License gives no |
|||
permission to license the work in any other way, but it does not |
|||
invalidate such permission if you have separately received it. |
|||
|
|||
d) If the work has interactive user interfaces, each must display |
|||
Appropriate Legal Notices; however, if the Program has interactive |
|||
interfaces that do not display Appropriate Legal Notices, your |
|||
work need not make them do so. |
|||
|
|||
A compilation of a covered work with other separate and independent |
|||
works, which are not by their nature extensions of the covered work, |
|||
and which are not combined with it such as to form a larger program, |
|||
in or on a volume of a storage or distribution medium, is called an |
|||
"aggregate" if the compilation and its resulting copyright are not |
|||
used to limit the access or legal rights of the compilation's users |
|||
beyond what the individual works permit. Inclusion of a covered work |
|||
in an aggregate does not cause this License to apply to the other |
|||
parts of the aggregate. |
|||
|
|||
6. Conveying Non-Source Forms. |
|||
|
|||
You may convey a covered work in object code form under the terms |
|||
of sections 4 and 5, provided that you also convey the |
|||
machine-readable Corresponding Source under the terms of this License, |
|||
in one of these ways: |
|||
|
|||
a) Convey the object code in, or embodied in, a physical product |
|||
(including a physical distribution medium), accompanied by the |
|||
Corresponding Source fixed on a durable physical medium |
|||
customarily used for software interchange. |
|||
|
|||
b) Convey the object code in, or embodied in, a physical product |
|||
(including a physical distribution medium), accompanied by a |
|||
written offer, valid for at least three years and valid for as |
|||
long as you offer spare parts or customer support for that product |
|||
model, to give anyone who possesses the object code either (1) a |
|||
copy of the Corresponding Source for all the software in the |
|||
product that is covered by this License, on a durable physical |
|||
medium customarily used for software interchange, for a price no |
|||
more than your reasonable cost of physically performing this |
|||
conveying of source, or (2) access to copy the |
|||
Corresponding Source from a network server at no charge. |
|||
|
|||
c) Convey individual copies of the object code with a copy of the |
|||
written offer to provide the Corresponding Source. This |
|||
alternative is allowed only occasionally and noncommercially, and |
|||
only if you received the object code with such an offer, in accord |
|||
with subsection 6b. |
|||
|
|||
d) Convey the object code by offering access from a designated |
|||
place (gratis or for a charge), and offer equivalent access to the |
|||
Corresponding Source in the same way through the same place at no |
|||
further charge. You need not require recipients to copy the |
|||
Corresponding Source along with the object code. If the place to |
|||
copy the object code is a network server, the Corresponding Source |
|||
may be on a different server (operated by you or a third party) |
|||
that supports equivalent copying facilities, provided you maintain |
|||
clear directions next to the object code saying where to find the |
|||
Corresponding Source. Regardless of what server hosts the |
|||
Corresponding Source, you remain obligated to ensure that it is |
|||
available for as long as needed to satisfy these requirements. |
|||
|
|||
e) Convey the object code using peer-to-peer transmission, provided |
|||
you inform other peers where the object code and Corresponding |
|||
Source of the work are being offered to the general public at no |
|||
charge under subsection 6d. |
|||
|
|||
A separable portion of the object code, whose source code is excluded |
|||
from the Corresponding Source as a System Library, need not be |
|||
included in conveying the object code work. |
|||
|
|||
A "User Product" is either (1) a "consumer product", which means any |
|||
tangible personal property which is normally used for personal, family, |
|||
or household purposes, or (2) anything designed or sold for incorporation |
|||
into a dwelling. In determining whether a product is a consumer product, |
|||
doubtful cases shall be resolved in favor of coverage. For a particular |
|||
product received by a particular user, "normally used" refers to a |
|||
typical or common use of that class of product, regardless of the status |
|||
of the particular user or of the way in which the particular user |
|||
actually uses, or expects or is expected to use, the product. A product |
|||
is a consumer product regardless of whether the product has substantial |
|||
commercial, industrial or non-consumer uses, unless such uses represent |
|||
the only significant mode of use of the product. |
|||
|
|||
"Installation Information" for a User Product means any methods, |
|||
procedures, authorization keys, or other information required to install |
|||
and execute modified versions of a covered work in that User Product from |
|||
a modified version of its Corresponding Source. The information must |
|||
suffice to ensure that the continued functioning of the modified object |
|||
code is in no case prevented or interfered with solely because |
|||
modification has been made. |
|||
|
|||
If you convey an object code work under this section in, or with, or |
|||
specifically for use in, a User Product, and the conveying occurs as |
|||
part of a transaction in which the right of possession and use of the |
|||
User Product is transferred to the recipient in perpetuity or for a |
|||
fixed term (regardless of how the transaction is characterized), the |
|||
Corresponding Source conveyed under this section must be accompanied |
|||
by the Installation Information. But this requirement does not apply |
|||
if neither you nor any third party retains the ability to install |
|||
modified object code on the User Product (for example, the work has |
|||
been installed in ROM). |
|||
|
|||
The requirement to provide Installation Information does not include a |
|||
requirement to continue to provide support service, warranty, or updates |
|||
for a work that has been modified or installed by the recipient, or for |
|||
the User Product in which it has been modified or installed. Access to a |
|||
network may be denied when the modification itself materially and |
|||
adversely affects the operation of the network or violates the rules and |
|||
protocols for communication across the network. |
|||
|
|||
Corresponding Source conveyed, and Installation Information provided, |
|||
in accord with this section must be in a format that is publicly |
|||
documented (and with an implementation available to the public in |
|||
source code form), and must require no special password or key for |
|||
unpacking, reading or copying. |
|||
|
|||
7. Additional Terms. |
|||
|
|||
"Additional permissions" are terms that supplement the terms of this |
|||
License by making exceptions from one or more of its conditions. |
|||
Additional permissions that are applicable to the entire Program shall |
|||
be treated as though they were included in this License, to the extent |
|||
that they are valid under applicable law. If additional permissions |
|||
apply only to part of the Program, that part may be used separately |
|||
under those permissions, but the entire Program remains governed by |
|||
this License without regard to the additional permissions. |
|||
|
|||
When you convey a copy of a covered work, you may at your option |
|||
remove any additional permissions from that copy, or from any part of |
|||
it. (Additional permissions may be written to require their own |
|||
removal in certain cases when you modify the work.) You may place |
|||
additional permissions on material, added by you to a covered work, |
|||
for which you have or can give appropriate copyright permission. |
|||
|
|||
Notwithstanding any other provision of this License, for material you |
|||
add to a covered work, you may (if authorized by the copyright holders of |
|||
that material) supplement the terms of this License with terms: |
|||
|
|||
a) Disclaiming warranty or limiting liability differently from the |
|||
terms of sections 15 and 16 of this License; or |
|||
|
|||
b) Requiring preservation of specified reasonable legal notices or |
|||
author attributions in that material or in the Appropriate Legal |
|||
Notices displayed by works containing it; or |
|||
|
|||
c) Prohibiting misrepresentation of the origin of that material, or |
|||
requiring that modified versions of such material be marked in |
|||
reasonable ways as different from the original version; or |
|||
|
|||
d) Limiting the use for publicity purposes of names of licensors or |
|||
authors of the material; or |
|||
|
|||
e) Declining to grant rights under trademark law for use of some |
|||
trade names, trademarks, or service marks; or |
|||
|
|||
f) Requiring indemnification of licensors and authors of that |
|||
material by anyone who conveys the material (or modified versions of |
|||
it) with contractual assumptions of liability to the recipient, for |
|||
any liability that these contractual assumptions directly impose on |
|||
those licensors and authors. |
|||
|
|||
All other non-permissive additional terms are considered "further |
|||
restrictions" within the meaning of section 10. If the Program as you |
|||
received it, or any part of it, contains a notice stating that it is |
|||
governed by this License along with a term that is a further |
|||
restriction, you may remove that term. If a license document contains |
|||
a further restriction but permits relicensing or conveying under this |
|||
License, you may add to a covered work material governed by the terms |
|||
of that license document, provided that the further restriction does |
|||
not survive such relicensing or conveying. |
|||
|
|||
If you add terms to a covered work in accord with this section, you |
|||
must place, in the relevant source files, a statement of the |
|||
additional terms that apply to those files, or a notice indicating |
|||
where to find the applicable terms. |
|||
|
|||
Additional terms, permissive or non-permissive, may be stated in the |
|||
form of a separately written license, or stated as exceptions; |
|||
the above requirements apply either way. |
|||
|
|||
8. Termination. |
|||
|
|||
You may not propagate or modify a covered work except as expressly |
|||
provided under this License. Any attempt otherwise to propagate or |
|||
modify it is void, and will automatically terminate your rights under |
|||
this License (including any patent licenses granted under the third |
|||
paragraph of section 11). |
|||
|
|||
However, if you cease all violation of this License, then your |
|||
license from a particular copyright holder is reinstated (a) |
|||
provisionally, unless and until the copyright holder explicitly and |
|||
finally terminates your license, and (b) permanently, if the copyright |
|||
holder fails to notify you of the violation by some reasonable means |
|||
prior to 60 days after the cessation. |
|||
|
|||
Moreover, your license from a particular copyright holder is |
|||
reinstated permanently if the copyright holder notifies you of the |
|||
violation by some reasonable means, this is the first time you have |
|||
received notice of violation of this License (for any work) from that |
|||
copyright holder, and you cure the violation prior to 30 days after |
|||
your receipt of the notice. |
|||
|
|||
Termination of your rights under this section does not terminate the |
|||
licenses of parties who have received copies or rights from you under |
|||
this License. If your rights have been terminated and not permanently |
|||
reinstated, you do not qualify to receive new licenses for the same |
|||
material under section 10. |
|||
|
|||
9. Acceptance Not Required for Having Copies. |
|||
|
|||
You are not required to accept this License in order to receive or |
|||
run a copy of the Program. Ancillary propagation of a covered work |
|||
occurring solely as a consequence of using peer-to-peer transmission |
|||
to receive a copy likewise does not require acceptance. However, |
|||
nothing other than this License grants you permission to propagate or |
|||
modify any covered work. These actions infringe copyright if you do |
|||
not accept this License. Therefore, by modifying or propagating a |
|||
covered work, you indicate your acceptance of this License to do so. |
|||
|
|||
10. Automatic Licensing of Downstream Recipients. |
|||
|
|||
Each time you convey a covered work, the recipient automatically |
|||
receives a license from the original licensors, to run, modify and |
|||
propagate that work, subject to this License. You are not responsible |
|||
for enforcing compliance by third parties with this License. |
|||
|
|||
An "entity transaction" is a transaction transferring control of an |
|||
organization, or substantially all assets of one, or subdividing an |
|||
organization, or merging organizations. If propagation of a covered |
|||
work results from an entity transaction, each party to that |
|||
transaction who receives a copy of the work also receives whatever |
|||
licenses to the work the party's predecessor in interest had or could |
|||
give under the previous paragraph, plus a right to possession of the |
|||
Corresponding Source of the work from the predecessor in interest, if |
|||
the predecessor has it or can get it with reasonable efforts. |
|||
|
|||
You may not impose any further restrictions on the exercise of the |
|||
rights granted or affirmed under this License. For example, you may |
|||
not impose a license fee, royalty, or other charge for exercise of |
|||
rights granted under this License, and you may not initiate litigation |
|||
(including a cross-claim or counterclaim in a lawsuit) alleging that |
|||
any patent claim is infringed by making, using, selling, offering for |
|||
sale, or importing the Program or any portion of it. |
|||
|
|||
11. Patents. |
|||
|
|||
A "contributor" is a copyright holder who authorizes use under this |
|||
License of the Program or a work on which the Program is based. The |
|||
work thus licensed is called the contributor's "contributor version". |
|||
|
|||
A contributor's "essential patent claims" are all patent claims |
|||
owned or controlled by the contributor, whether already acquired or |
|||
hereafter acquired, that would be infringed by some manner, permitted |
|||
by this License, of making, using, or selling its contributor version, |
|||
but do not include claims that would be infringed only as a |
|||
consequence of further modification of the contributor version. For |
|||
purposes of this definition, "control" includes the right to grant |
|||
patent sublicenses in a manner consistent with the requirements of |
|||
this License. |
|||
|
|||
Each contributor grants you a non-exclusive, worldwide, royalty-free |
|||
patent license under the contributor's essential patent claims, to |
|||
make, use, sell, offer for sale, import and otherwise run, modify and |
|||
propagate the contents of its contributor version. |
|||
|
|||
In the following three paragraphs, a "patent license" is any express |
|||
agreement or commitment, however denominated, not to enforce a patent |
|||
(such as an express permission to practice a patent or covenant not to |
|||
sue for patent infringement). To "grant" such a patent license to a |
|||
party means to make such an agreement or commitment not to enforce a |
|||
patent against the party. |
|||
|
|||
If you convey a covered work, knowingly relying on a patent license, |
|||
and the Corresponding Source of the work is not available for anyone |
|||
to copy, free of charge and under the terms of this License, through a |
|||
publicly available network server or other readily accessible means, |
|||
then you must either (1) cause the Corresponding Source to be so |
|||
available, or (2) arrange to deprive yourself of the benefit of the |
|||
patent license for this particular work, or (3) arrange, in a manner |
|||
consistent with the requirements of this License, to extend the patent |
|||
license to downstream recipients. "Knowingly relying" means you have |
|||
actual knowledge that, but for the patent license, your conveying the |
|||
covered work in a country, or your recipient's use of the covered work |
|||
in a country, would infringe one or more identifiable patents in that |
|||
country that you have reason to believe are valid. |
|||
|
|||
If, pursuant to or in connection with a single transaction or |
|||
arrangement, you convey, or propagate by procuring conveyance of, a |
|||
covered work, and grant a patent license to some of the parties |
|||
receiving the covered work authorizing them to use, propagate, modify |
|||
or convey a specific copy of the covered work, then the patent license |
|||
you grant is automatically extended to all recipients of the covered |
|||
work and works based on it. |
|||
|
|||
A patent license is "discriminatory" if it does not include within |
|||
the scope of its coverage, prohibits the exercise of, or is |
|||
conditioned on the non-exercise of one or more of the rights that are |
|||
specifically granted under this License. You may not convey a covered |
|||
work if you are a party to an arrangement with a third party that is |
|||
in the business of distributing software, under which you make payment |
|||
to the third party based on the extent of your activity of conveying |
|||
the work, and under which the third party grants, to any of the |
|||
parties who would receive the covered work from you, a discriminatory |
|||
patent license (a) in connection with copies of the covered work |
|||
conveyed by you (or copies made from those copies), or (b) primarily |
|||
for and in connection with specific products or compilations that |
|||
contain the covered work, unless you entered into that arrangement, |
|||
or that patent license was granted, prior to 28 March 2007. |
|||
|
|||
Nothing in this License shall be construed as excluding or limiting |
|||
any implied license or other defenses to infringement that may |
|||
otherwise be available to you under applicable patent law. |
|||
|
|||
12. No Surrender of Others' Freedom. |
|||
|
|||
If conditions are imposed on you (whether by court order, agreement or |
|||
otherwise) that contradict the conditions of this License, they do not |
|||
excuse you from the conditions of this License. If you cannot convey a |
|||
covered work so as to satisfy simultaneously your obligations under this |
|||
License and any other pertinent obligations, then as a consequence you may |
|||
not convey it at all. For example, if you agree to terms that obligate you |
|||
to collect a royalty for further conveying from those to whom you convey |
|||
the Program, the only way you could satisfy both those terms and this |
|||
License would be to refrain entirely from conveying the Program. |
|||
|
|||
13. Use with the GNU Affero General Public License. |
|||
|
|||
Notwithstanding any other provision of this License, you have |
|||
permission to link or combine any covered work with a work licensed |
|||
under version 3 of the GNU Affero General Public License into a single |
|||
combined work, and to convey the resulting work. The terms of this |
|||
License will continue to apply to the part which is the covered work, |
|||
but the special requirements of the GNU Affero General Public License, |
|||
section 13, concerning interaction through a network will apply to the |
|||
combination as such. |
|||
|
|||
14. Revised Versions of this License. |
|||
|
|||
The Free Software Foundation may publish revised and/or new versions of |
|||
the GNU General Public License from time to time. Such new versions will |
|||
be similar in spirit to the present version, but may differ in detail to |
|||
address new problems or concerns. |
|||
|
|||
Each version is given a distinguishing version number. If the |
|||
Program specifies that a certain numbered version of the GNU General |
|||
Public License "or any later version" applies to it, you have the |
|||
option of following the terms and conditions either of that numbered |
|||
version or of any later version published by the Free Software |
|||
Foundation. If the Program does not specify a version number of the |
|||
GNU General Public License, you may choose any version ever published |
|||
by the Free Software Foundation. |
|||
|
|||
If the Program specifies that a proxy can decide which future |
|||
versions of the GNU General Public License can be used, that proxy's |
|||
public statement of acceptance of a version permanently authorizes you |
|||
to choose that version for the Program. |
|||
|
|||
Later license versions may give you additional or different |
|||
permissions. However, no additional obligations are imposed on any |
|||
author or copyright holder as a result of your choosing to follow a |
|||
later version. |
|||
|
|||
15. Disclaimer of Warranty. |
|||
|
|||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
|||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
|||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
|||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
|||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
|||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
|||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
|||
|
|||
16. Limitation of Liability. |
|||
|
|||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
|||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
|||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
|||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
|||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
|||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
|||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
|||
SUCH DAMAGES. |
|||
|
|||
17. Interpretation of Sections 15 and 16. |
|||
|
|||
If the disclaimer of warranty and limitation of liability provided |
|||
above cannot be given local legal effect according to their terms, |
|||
reviewing courts shall apply local law that most closely approximates |
|||
an absolute waiver of all civil liability in connection with the |
|||
Program, unless a warranty or assumption of liability accompanies a |
|||
copy of the Program in return for a fee. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
How to Apply These Terms to Your New Programs |
|||
|
|||
If you develop a new program, and you want it to be of the greatest |
|||
possible use to the public, the best way to achieve this is to make it |
|||
free software which everyone can redistribute and change under these terms. |
|||
|
|||
To do so, attach the following notices to the program. It is safest |
|||
to attach them to the start of each source file to most effectively |
|||
state the exclusion of warranty; and each file should have at least |
|||
the "copyright" line and a pointer to where the full notice is found. |
|||
|
|||
<one line to give the program's name and a brief idea of what it does.> |
|||
Copyright (C) <year> <name of author> |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
Also add information on how to contact you by electronic and paper mail. |
|||
|
|||
If the program does terminal interaction, make it output a short |
|||
notice like this when it starts in an interactive mode: |
|||
|
|||
<program> Copyright (C) <year> <name of author> |
|||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
|||
This is free software, and you are welcome to redistribute it |
|||
under certain conditions; type `show c' for details. |
|||
|
|||
The hypothetical commands `show w' and `show c' should show the appropriate |
|||
parts of the General Public License. Of course, your program's commands |
|||
might be different; for a GUI interface, you would use an "about box". |
|||
|
|||
You should also get your employer (if you work as a programmer) or school, |
|||
if any, to sign a "copyright disclaimer" for the program, if necessary. |
|||
For more information on this, and how to apply and follow the GNU GPL, see |
|||
<http://www.gnu.org/licenses/>. |
|||
|
|||
The GNU General Public License does not permit incorporating your program |
|||
into proprietary programs. If your program is a subroutine library, you |
|||
may consider it more useful to permit linking proprietary applications with |
|||
the library. If this is what you want to do, use the GNU Lesser General |
|||
Public License instead of this License. But first, please read |
|||
<http://www.gnu.org/philosophy/why-not-lgpl.html>. |
@ -0,0 +1,43 @@ |
|||
AUTOCONF CONFIGURE SCRIPT EXCEPTION |
|||
Version 3.0, 18 August 2009 |
|||
|
|||
Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/> |
|||
Everyone is permitted to copy and distribute verbatim copies |
|||
of this license document, but changing it is not allowed. |
|||
|
|||
This Exception is an additional permission under section 7 of the GNU |
|||
General Public License, version 3 ("GPLv3"). It applies to a given |
|||
file that bears a notice placed by the copyright holder of the file |
|||
stating that the file is governed by GPLv3 along with this Exception. |
|||
|
|||
The purpose of this Exception is to allow distribution of Autoconf's |
|||
typical output under terms of the recipient's choice (including |
|||
proprietary). |
|||
|
|||
0. Definitions |
|||
|
|||
"Covered Code" is the source or object code of a version of Autoconf |
|||
that is a covered work under this License. |
|||
|
|||
"Normally Copied Code" for a version of Autoconf means all parts of |
|||
its Covered Code which that version can copy from its code (i.e., not |
|||
from its input file) into its minimally verbose, non-debugging and |
|||
non-tracing output. |
|||
|
|||
"Ineligible Code" is Covered Code that is not Normally Copied Code. |
|||
|
|||
1. Grant of Additional Permission. |
|||
|
|||
You have permission to propagate output of Autoconf, even if such |
|||
propagation would otherwise violate the terms of GPLv3. However, if |
|||
by modifying Autoconf you cause any Ineligible Code of the version you |
|||
received to become Normally Copied Code of your modified version, then |
|||
you void this Exception for the resulting covered work. If you convey |
|||
that resulting covered work, you must remove this Exception in accordance |
|||
with the second paragraph of Section 7 of GPLv3. |
|||
|
|||
2. No Weakening of Autoconf Copyleft. |
|||
|
|||
The availability of this Exception does not imply any general presumption |
|||
that third-party software is unaffected by the copyleft requirements of |
|||
the license of Autoconf. |
@ -0,0 +1,87 @@ |
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
==================================================== |
|||
|
|||
|
|||
License / Copying (ffuzzy++) |
|||
----------------------------- |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
|
|||
License / Copying (configuration scripts) |
|||
------------------------------------------ |
|||
|
|||
This program is free software; you can redistribute it and/or modify it |
|||
under the terms of the GNU General Public License as published by the |
|||
Free Software Foundation, either version 3 of the License, or (at your |
|||
option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, but |
|||
WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
Under Section 7 of GPL version 3, you are granted additional |
|||
permissions described in the Autoconf Configure Script Exception, |
|||
version 3.0, as published by the Free Software Foundation. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
and a copy of the Autoconf Configure Script Exception along with |
|||
this program; see the files COPYING.GPLv3 and COPYING.GPLv3Autoconf |
|||
respectively. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
|
|||
License Files |
|||
-------------- |
|||
|
|||
* COPYING.GPLv3 |
|||
GNU General Public License, version 3 |
|||
* COPYING.GPLv3Autoconf |
|||
Autoconf Configure Script Exception, version 3 |
|||
* COPYING.GPLv2 |
|||
GNU General Public License, version 2 |
|||
* COPYING.Boost |
|||
Boost Software License, version 1.0 |
|||
|
|||
|
|||
Credits |
|||
-------- |
|||
|
|||
The most part of the source code are copied from fuzzy.c |
|||
in ssdeep version 2.11 (heavily modified though). |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
fuzzy.c (in ssdeep 2.11) is licensed under the terms of the |
|||
GNU General Public License as published by the Free Software Foundation; |
|||
either version 2 of the License, or (at your option) any later version. |
|||
|
|||
The indel distance code is contributed from kikairoya on Github. |
|||
|
|||
Copyright (C) 2014 kikairoya <kikairoya@gmail.com> |
|||
|
|||
This portion of code is licensed under the terms of the |
|||
Boost Software License, version 1.0 (compatible with GPLv2+). |
|||
|
|||
Tsukasa OI (the original author of libffuzzy and ffuzzy++) modified |
|||
them and wrote a fast implementation. |
|||
|
|||
Copyright (C) 2014, 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
The license of the modified portion may vary by place |
|||
(mostly licensed under the ISC license). |
|||
|
|||
See the source code for details. |
@ -0,0 +1,64 @@ |
|||
#
|
|||
#
|
|||
# ffuzzy++ : C++ implementation of fast fuzzy hashing
|
|||
#
|
|||
# Makefile.am
|
|||
# Makefile template for top directory
|
|||
#
|
|||
# Copyright (C) 2015 Tsukasa OI.
|
|||
#
|
|||
#
|
|||
# Permission to use, copy, modify, and/or distribute this software for
|
|||
# any purpose with or without fee is hereby granted, provided that the
|
|||
# above copyright notice and this permission notice appear in all copies.
|
|||
#
|
|||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#
|
|||
#
|
|||
if ENABLE_STATIC_LIB |
|||
lib_LIBRARIES = libffuzzypp.a |
|||
libffuzzypp_a_SOURCES = ffuzzy_declarations.cpp |
|||
endif |
|||
nobase_include_HEADERS = \
|
|||
ffuzzy.hpp \
|
|||
ffuzzypp/base64.hpp \
|
|||
ffuzzypp/context_hash.hpp \
|
|||
ffuzzypp/context_hash_fast.hpp \
|
|||
ffuzzypp/digest.hpp \
|
|||
ffuzzypp/digest_base.hpp \
|
|||
ffuzzypp/digest_blocksize.hpp \
|
|||
ffuzzypp/digest_comparison.hpp \
|
|||
ffuzzypp/digest_data.hpp \
|
|||
ffuzzypp/digest_filesize.hpp \
|
|||
ffuzzypp/digest_generator.hpp \
|
|||
ffuzzypp/digest_position_array.hpp \
|
|||
ffuzzypp/digest_position_array_base.hpp \
|
|||
ffuzzypp/rolling_hash.hpp \
|
|||
ffuzzypp/rolling_hash_ssdeep.hpp \
|
|||
ffuzzypp/strings/common_substr.hpp \
|
|||
ffuzzypp/strings/edit_dist.hpp \
|
|||
ffuzzypp/strings/nosequences.hpp \
|
|||
ffuzzypp/strings/position_array.hpp \
|
|||
ffuzzypp/strings/sequences.hpp \
|
|||
ffuzzypp/strings/terminators.hpp \
|
|||
ffuzzypp/strings/transform.hpp \
|
|||
ffuzzypp/utils/likely.hpp \
|
|||
ffuzzypp/utils/minmax.hpp \
|
|||
ffuzzypp/utils/numeric_digits.hpp \
|
|||
ffuzzypp/utils/ranges.hpp \
|
|||
ffuzzypp/utils/safe_int.hpp \
|
|||
ffuzzypp/utils/static_assert_query.hpp \
|
|||
ffuzzypp/utils/type_modifier.hpp |
|||
EXTRA_DIST = \
|
|||
bootstrap.sh \
|
|||
README.md NEWS.md \
|
|||
COPYING.md COPYING.GPLv2 COPYING.Boost \
|
|||
COPYING.GPLv3 COPYING.GPLv3Autoconf \
|
|||
.gitignore .gitattributes ext/.gitignore |
|||
SUBDIRS = examples tests |
@ -0,0 +1,30 @@ |
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
==================================================== |
|||
|
|||
|
|||
Version 4.0.1 - 2017-06-28 |
|||
--------------------------- |
|||
|
|||
* `make dist` issue is now fixed. |
|||
|
|||
|
|||
Version 4.0 - 2017-06-28 |
|||
------------------------- |
|||
|
|||
* NEW: Bit-parallelism |
|||
(clustering can be over 10 times faster than version 3.0) |
|||
* Digest generator is optimized |
|||
(can be twice as fast as version 3.0) |
|||
* Utility methods are added |
|||
|
|||
|
|||
Version 3.0.1 - 2017-06-04 |
|||
--------------------------- |
|||
|
|||
* Uses of deprecates type trait are now replaced with decent ones. |
|||
|
|||
|
|||
Version 3.0 - 2015-04-25 |
|||
------------------------- |
|||
|
|||
* The initial public release |
@ -0,0 +1,229 @@ |
|||
ffuzzy++ : C++ implementation of ssdeep-compatible fast fuzzy hashing |
|||
====================================================================== |
|||
|
|||
|
|||
|
|||
What is ffuzzy++? |
|||
------------------ |
|||
|
|||
ffuzzy++ is a fuzzy hash implementation compatible with |
|||
ssdeep version 2.13 (<http://ssdeep.sourceforge.net/>). |
|||
Using this implementation, you can: |
|||
|
|||
* Generate fuzzy digests from files and buffers |
|||
* Compare fuzzy digests generated from ffuzzy++ or |
|||
other ssdeep-compatible implementations |
|||
|
|||
This implementation is designed to be fast, stable and thread-safe. |
|||
Sometimes this is about 60% to 120% faster than ssdeep when you |
|||
cluster digests and is about 10% to 60% faster than ssdeep when |
|||
you generate digests. |
|||
|
|||
However, this C++ implementation is primarily designed to be fast |
|||
by exposing internal structure of fuzzy hashing. So this |
|||
implementation may not be safe as you think (depends on function |
|||
you use). Instead, you can boost your clustering application |
|||
using efficient digest utilities. |
|||
|
|||
|
|||
|
|||
Requirements |
|||
------------- |
|||
|
|||
You will need C++ compiler with full C++11 support. |
|||
|
|||
You will also need GNU Autoconf Archive when you build `configure` |
|||
script (<https://www.gnu.org/software/autoconf-archive/>). |
|||
|
|||
If you wish to run the test, Google C++ Testing Framework |
|||
(<https://code.google.com/p/googletest/>) will be needed. |
|||
|
|||
|
|||
|
|||
Installation |
|||
------------- |
|||
|
|||
This program can be installed without proper "installation". |
|||
Just copy `ffuzzy.hpp` and `ffuzzypp` directory to anywhere you want. |
|||
In this case, you need to define `FFUZZYPP_DECLARATIONS` before |
|||
`ffuzzy.hpp` in **one** of the source files to make the linker happy. |
|||
|
|||
You can compile and install a static helper library `libffuzzypp.a` |
|||
by running `./configure && make && make install`. |
|||
If you don't have `configure` script on the top directory, you need |
|||
to run `./bootstrap.sh` to make build files. |
|||
|
|||
|
|||
|
|||
Configuration Options |
|||
---------------------- |
|||
|
|||
* --disable-static-lib |
|||
Disable building `libffuzzypp.a` |
|||
* --disable-debug |
|||
Disable assertions on examples and tests. |
|||
Don't disable assertions if you wish to test ffuzzy++. |
|||
* --disable-position-array |
|||
Disable using bit-parallel algorithms on examples and tests. |
|||
* --enable-examples |
|||
Enable building examples (see "Building Examples" section) |
|||
* --enable-tests |
|||
Enable building test cases (see "Running Tests" section) |
|||
* --disable-compatibility-tests |
|||
Ignore ssdeep-specific parameters during compilation of |
|||
test cases. This option prevents compiling compatibility tests. |
|||
|
|||
|
|||
|
|||
Building Examples |
|||
------------------ |
|||
|
|||
There are two examples: |
|||
|
|||
* examples/compare-hash |
|||
You can use this program to compare two fuzzy digests. |
|||
* examples/compute-hash |
|||
You can use this program to compute fuzzy digests from |
|||
given files. |
|||
|
|||
|
|||
|
|||
Running Tests |
|||
-------------- |
|||
|
|||
By default, compatibility tests (which depends on ssdeep-specific |
|||
parameters) are enabled and you can disable these tests by adding |
|||
`--disable-compatibility-tests` configuration option. |
|||
|
|||
* tests/test-small |
|||
Relatively small tests (that can be done in a few minutes). |
|||
* tests/test-compatibility-small |
|||
Compatibility tests (depends on ssdeep parameters) |
|||
* tests/test-compatibility-large |
|||
Large compatibility tests (depends on ssdeep parameters) |
|||
* tests/test-precond |
|||
Precondition tests (it always succeeds at runtime because |
|||
all "tests" are performed while compiling this executable). |
|||
|
|||
|
|||
|
|||
Configuration |
|||
-------------- |
|||
|
|||
Defining certain macros before including `ffuzzy.hpp` can change some |
|||
behavior of this library. |
|||
|
|||
* `FFUZZYPP_DEBUG` |
|||
This macro enables assertions for debugging. |
|||
* `FFUZZYPP_DISABLE_POSITION_ARRAY` |
|||
This macro disables using bit-parallel algorithms. |
|||
|
|||
|
|||
|
|||
Caution |
|||
-------- |
|||
|
|||
* This package is intended for static-linking to your program. |
|||
* Packaging this implementation **alone** for OS distribution |
|||
is not recommended. The interface may change without notice. |
|||
* Digests generated from files equal to or larger than 4GiB |
|||
are not portable enough. See following secions for details. |
|||
|
|||
|
|||
|
|||
Differences between ssdeep |
|||
--------------------------- |
|||
|
|||
When comparing identical digests, ssdeep 2.9 and 2.13 return different |
|||
values. By default, ffuzzy++ emulates behavior of version 2.13. |
|||
If you give `comparison_version::v2_9` to `comparison`-related function |
|||
as a template parameter, ffuzzy++ will emulate behavior of version 2.9. |
|||
|
|||
Result of comparison is not in a signed type but in an unsigned type |
|||
because error handling things are moved out of comparison. |
|||
So comparison functions cannot return -1 on error (like ssdeep/libfuzzy). |
|||
Instead, digest parser (which will be used before comparison) will raise |
|||
an exception when it fails to parse digest string. |
|||
|
|||
|
|||
|
|||
Differences between ssdeep -2.12 (DETAILS) |
|||
------------------------------------------- |
|||
|
|||
Because of current implementation restrictions in ssdeep 2.12 and before |
|||
(not in ffuzzy++), the digest and comparison result may different when: |
|||
|
|||
* Extremely large files are given (for example): |
|||
* For files equal to or larger than 4GiB, |
|||
ssdeep 2.10-2.12 on 32-bit platform and ssdeep -2.9 |
|||
causes arithmetic overflow while handling file sizes. |
|||
This will result in unexpected behavior. |
|||
* For files larger than 4GiB, |
|||
ssdeep -2.12's rolling hash implementation is not exactly |
|||
rolling. This will result in incorrect digest value. |
|||
* For files larger than 96GiB, |
|||
ssdeep 2.10-2.12 will crash under some circumstances. |
|||
ssdeep 2.9 with 64-bit file size patch will use incorrect value |
|||
(because of arithmetic overflow) when generating digests. |
|||
ffuzzy++'s implementation matches mathematical properties |
|||
of ssdeep digest generation algorithm while preventing |
|||
arithmetic overflow bug on ssdeep 2.9 with |
|||
64-bit file size patch. |
|||
* For files equal to or larger than 2^64 bytes, |
|||
ssdeep 2.10-2.12 on 64-bit platform will cause arithmetic |
|||
overflow on file sizes. Although we don't accept |
|||
files larger than 192GiB (see "Limitations" section), ssdeep |
|||
-2.12 doesn't have mechanism to prevent file size overflow. |
|||
* Some digests generated from extremely large files are given: |
|||
* For digests generated from files larger than 12GiB, |
|||
ssdeep -2.12 may cause arithmetic overflow while comparing |
|||
digests. This may make incorrect comparison score of zero. |
|||
|
|||
Overrall, ffuzzy++ is almost compatible with ssdeep -2.12. But on some |
|||
circumstances which ssdeep will fail or did not support (yes, actually |
|||
files equal to or larger than 4GiB are not officially supported by |
|||
ssdeep -2.12), ffuzzy++ returns mostly legit values |
|||
(based on mathematical properties of ssdeep). |
|||
|
|||
|
|||
|
|||
Differences between ssdeep 2.13 |
|||
-------------------------------- |
|||
|
|||
ssdeep 2.13 behaves very similar to ffuzzy++ due to bug fixes. |
|||
There are few implementation differences but behaves the same when: |
|||
|
|||
* Generating fuzzy digests from arbitrary input |
|||
* Comparing digests generated by new implementations |
|||
* Comparing digests generated from files up to 96GiB |
|||
by previous ssdeep versions |
|||
|
|||
|
|||
|
|||
Limitations |
|||
------------ |
|||
|
|||
Both implementations (ssdeep and ffuzzy++) have a limit of 192GiB in |
|||
digest generator. Based on mathematical properties of ssdeep, |
|||
we **could** accept much larger files. However, digests generated |
|||
from such files are nearly useless (because resulting digests will |
|||
not represent signature of whole file contents). |
|||
|
|||
So I decided not to accept files larger than 192GiB on ffuzzy++ |
|||
as well as ssdeep 2.10+. |
|||
|
|||
Although ffuzzy++ implementation is completely platform-independent, |
|||
ssdeep digest for files equal to or larger than 4GiB are still not |
|||
portable enough (varies by ssdeep versions and platforms). |
|||
|
|||
Make sure that you use new implementations (ssdeep 2.13 or ffuzzy++) |
|||
when you accept extremely large files. If you use older versions of |
|||
ssdeep, please consider rejecting such big files (equal to or larger |
|||
than 4GiB) when you generate digests. |
|||
|
|||
|
|||
|
|||
License / Copying |
|||
------------------ |
|||
|
|||
See `COPYING.md` file for details. |
@ -0,0 +1,2 @@ |
|||
#! /bin/sh |
|||
autoreconf -f -i |
@ -0,0 +1,94 @@ |
|||
# |
|||
# |
|||
# ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
# |
|||
# configure.ac |
|||
# Configuration script |
|||
# |
|||
# Copyright (C) 2017 Tsukasa OI. |
|||
# |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify it |
|||
# under the terms of the GNU General Public License as published by the |
|||
# Free Software Foundation, either version 3 of the License, or (at your |
|||
# option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, but |
|||
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# Under Section 7 of GPL version 3, you are granted additional |
|||
# permissions described in the Autoconf Configure Script Exception, |
|||
# version 3.0, as published by the Free Software Foundation. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# and a copy of the Autoconf Configure Script Exception along with |
|||
# this program; see the files COPYING.GPLv3 and COPYING.GPLv3Autoconf |
|||
# respectively. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
# |
|||
AC_PREREQ([2.65]) |
|||
AC_INIT([ffuzzypp], [4.0.1], [floss_ssdeep@irq.a4lg.com]) |
|||
AC_CONFIG_SRCDIR([ffuzzy_declarations.cpp]) |
|||
AC_CONFIG_AUX_DIR([ext]) |
|||
AM_INIT_AUTOMAKE([foreign dist-xz]) |
|||
|
|||
dnl --disable-static-lib |
|||
AC_ARG_ENABLE([static-lib], |
|||
[AS_HELP_STRING([--disable-static-lib],[disable compiling static library libffuzzypp.a])],, |
|||
[enable_static_lib=yes]) |
|||
AM_CONDITIONAL([ENABLE_STATIC_LIB],[test "x$enable_static_lib" != xno]) |
|||
|
|||
dnl --disable-debug |
|||
AC_ARG_ENABLE([debug], |
|||
[AS_HELP_STRING([--disable-debug],[disable debugging support on examples and tests])],, |
|||
[enable_debug=yes]) |
|||
if test "x$enable_debug" != xno |
|||
then |
|||
AC_DEFINE([FFUZZYPP_DEBUG], [1], [enable testing this implementation]) |
|||
fi |
|||
|
|||
dnl --disable-position-array |
|||
AC_ARG_ENABLE([position-array], |
|||
[AS_HELP_STRING([--disable-position-array],[disable using bit-parallel algorithms on examples and tests])],, |
|||
[enable_position_array=yes]) |
|||
if test "x$enable_position_array" = xno |
|||
then |
|||
AC_DEFINE([FFUZZYPP_DISABLE_POSITION_ARRAY], [1], [disable bit-parallel algorithms]) |
|||
fi |
|||
|
|||
dnl --enable-examples |
|||
AC_ARG_ENABLE([examples], |
|||
[AS_HELP_STRING([--enable-examples],[enable building examples (will not be installed)])],, |
|||
[enable_examples=no]) |
|||
AM_CONDITIONAL([ENABLE_EXAMPLES],[test "x$enable_examples" != xno]) |
|||
|
|||
dnl --enable-tests |
|||
AC_ARG_ENABLE([tests], |
|||
[AS_HELP_STRING([--enable-tests],[enable runtime tests])],, |
|||
[enable_tests=no]) |
|||
AM_CONDITIONAL([ENABLE_TESTS],[test "x$enable_tests" != xno]) |
|||
|
|||
dnl --disable-compatibility-tests |
|||
AC_ARG_ENABLE([compatibility-tests], |
|||
[AS_HELP_STRING([--disable-compatibility-tests],[disable compatibility tests])],, |
|||
[enable_compatibility_tests=yes]) |
|||
AM_CONDITIONAL([ENABLE_COMPATIBILITY_TESTS],[test "x$enable_compatibility_tests" != xno]) |
|||
|
|||
dnl C++11 compilers and required libraries (when testing) |
|||
AC_LANG([C++]) |
|||
AX_CXX_COMPILE_STDCXX_11 |
|||
AC_PROG_CXXCPP |
|||
if test "x$enable_static_lib" != xno |
|||
then |
|||
AC_PROG_RANLIB |
|||
fi |
|||
if test "x$enable_tests" != xno |
|||
then |
|||
AC_CHECK_HEADER([gtest/gtest.h],,[AC_MSG_ERROR([gtest/gtest.h from Google Test is required to build tests.])]) |
|||
AC_CHECK_LIB([gtest],[main],[:],[AC_MSG_ERROR([library gtest not found.])]) |
|||
AC_CHECK_LIB([gtest_main],[main],[:],[AC_MSG_ERROR([library gtest_main not found.])]) |
|||
fi |
|||
|
|||
AC_OUTPUT([Makefile examples/Makefile tests/Makefile]) |
@ -0,0 +1,4 @@ |
|||
* |
|||
!.gitignore |
|||
!Makefile.am |
|||
!*.cpp |
@ -0,0 +1,31 @@ |
|||
#
|
|||
#
|
|||
# ffuzzy++ : C++ implementation of fast fuzzy hashing
|
|||
#
|
|||
# examples/Makefile.am
|
|||
# Makefile template for examples
|
|||
#
|
|||
# Copyright (C) 2014 Tsukasa OI.
|
|||
#
|
|||
#
|
|||
# Permission to use, copy, modify, and/or distribute this software for
|
|||
# any purpose with or without fee is hereby granted, provided that the
|
|||
# above copyright notice and this permission notice appear in all copies.
|
|||
#
|
|||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#
|
|||
#
|
|||
AM_CPPFLAGS = -I$(top_srcdir) |
|||
|
|||
if ENABLE_EXAMPLES |
|||
noinst_PROGRAMS = compute-hash compare-hash |
|||
compute_hash_SOURCES = compute-hash.cpp |
|||
compare_hash_SOURCES = compare-hash.cpp |
|||
endif |
|||
EXTRA_DIST = .gitignore |
@ -0,0 +1,130 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ examples |
|||
|
|||
compare-hash.cpp |
|||
Fuzzy hash comparison program |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
|
|||
#include <cstdio> |
|||
|
|||
/*
|
|||
This enables compilation without libffuzzypp.a. |
|||
Define this in **one** of the source files to make the linker happy. |
|||
*/ |
|||
#define FFUZZYPP_DECLARATIONS |
|||
|
|||
// the following line will activate assertions.
|
|||
//#define FFUZZYPP_DEBUG
|
|||
|
|||
#include "ffuzzy.hpp" |
|||
using namespace ffuzzy; |
|||
|
|||
/*
|
|||
Both "digest_unorm_t" and "digest_t" are standard layout types and |
|||
contains exactly same sequences. |
|||
This means you can contain these data in the single union |
|||
to save memory. Note that some of operations are still invalid |
|||
because "normalized" type requires digest buffer normalized. |
|||
*/ |
|||
typedef union |
|||
{ |
|||
digest_unorm_t u; |
|||
digest_t d; |
|||
} unified_digest_t; |
|||
typedef union |
|||
{ |
|||
digest_long_unorm_t u; |
|||
digest_long_t d; |
|||
} unified_digest_long_t; |
|||
|
|||
int main(int argc, char** argv) |
|||
{ |
|||
if (argc != 3) |
|||
{ |
|||
fprintf(stderr, "usage: %s HASH1 HASH2\n", argv[0]); |
|||
return 1; |
|||
} |
|||
#if 1 |
|||
unified_digest_t h1, h2; |
|||
#else |
|||
unified_digest_long_t h1, h2; |
|||
#endif |
|||
char digestbuf[decltype(h1.u)::max_natural_chars]; |
|||
|
|||
// Parse digests
|
|||
if (!decltype(h1.u)::parse(h1.u, argv[1])) |
|||
{ |
|||
fprintf(stderr, "error: failed to parse HASH1.\n"); |
|||
return 1; |
|||
} |
|||
if (!decltype(h2.u)::parse(h2.u, argv[2])) |
|||
{ |
|||
fprintf(stderr, "error: failed to parse HASH2.\n"); |
|||
return 1; |
|||
} |
|||
|
|||
/*
|
|||
Restringize digests (just for demo) |
|||
Notice that we're using h1.d instead of h1.u? |
|||
This is not a good example but works perfectly. |
|||
*/ |
|||
if (!h1.d.pretty_unsafe(digestbuf)) |
|||
{ |
|||
fprintf(stderr, "abort: failed to re-stringize HASH1.\n"); |
|||
return 1; |
|||
} |
|||
printf("HASH1 : %s\n", digestbuf); |
|||
if (!h2.d.pretty_unsafe(digestbuf)) |
|||
{ |
|||
fprintf(stderr, "abort: failed to re-stringize HASH2.\n"); |
|||
return 1; |
|||
} |
|||
printf("HASH2 : %s\n", digestbuf); |
|||
|
|||
// Normalize digests and restringize them
|
|||
decltype(h1.d)::normalize(h1.d, h1.u); |
|||
decltype(h2.d)::normalize(h2.d, h2.u); |
|||
if (!h1.d.pretty_unsafe(digestbuf)) |
|||
{ |
|||
fprintf(stderr, "abort: failed to re-stringize HASH1.\n"); |
|||
return 1; |
|||
} |
|||
printf("NORM1 : %s\n", digestbuf); |
|||
if (!h2.d.pretty_unsafe(digestbuf)) |
|||
{ |
|||
fprintf(stderr, "abort: failed to re-stringize HASH2.\n"); |
|||
return 1; |
|||
} |
|||
printf("NORM2 : %s\n", digestbuf); |
|||
|
|||
/*
|
|||
Compare them |
|||
"Unnormalized" form has compare function but slow because of additional normalization) |
|||
|
|||
Note: |
|||
Use `compare` or `compare<comparison_version::latest>` for latest |
|||
version and `compare<comparison_version::v2_9>` for version 2.9 emulation. |
|||
*/ |
|||
digest_comparison_score_t score = |
|||
decltype(h1.d)::compare<comparison_version::v2_9>(h1.d, h2.d); |
|||
printf("SCORE: %u\n", unsigned(score)); // safe to cast to unsigned (value is in [0,100])
|
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,174 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ examples |
|||
|
|||
compute-hash.cpp |
|||
Fuzzy hash computation program |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
|
|||
#include <cstdio> |
|||
#include <limits> |
|||
|
|||
/*
|
|||
This enables compilation without libffuzzypp.a. |
|||
Define this in **one** of the source files to make the linker happy. |
|||
*/ |
|||
#define FFUZZYPP_DECLARATIONS |
|||
|
|||
// the following line will activate assertions.
|
|||
//#define FFUZZYPP_DEBUG
|
|||
|
|||
#include "ffuzzy.hpp" |
|||
using namespace ffuzzy; |
|||
|
|||
// the following line will enable constant file size optimization.
|
|||
#define OPTIMIZE_CONSTANT_FILE_SIZE |
|||
|
|||
|
|||
int main(int argc, char** argv) |
|||
{ |
|||
// digest_generator is reset by default.
|
|||
digest_generator gen; |
|||
// Use "unnormalized" form not to remove
|
|||
// sequences with 4 or more identical characters.
|
|||
// You can use "digest_t" and you will get digests without such sequences.
|
|||
digest_unorm_t d; |
|||
// Generated digest is always natural.
|
|||
// You don't have to give too large sizes.
|
|||
char digestbuf[digest_unorm_t::max_natural_chars]; |
|||
// File buffer
|
|||
static const size_t BUFFER_SIZE = 4096; |
|||
unsigned char filebuf[BUFFER_SIZE]; |
|||
|
|||
// The buffer which contains the format string.
|
|||
// Converting to "unsigned" is completely safe for
|
|||
// ssdeep-compatible configuration
|
|||
// (I mean, unless you haven't modified many parameters).
|
|||
static_assert(digest_unorm_t::max_natural_width <= std::numeric_limits<unsigned>::max(), |
|||
"digest_unorm_t::max_natural_width must be small enough."); |
|||
char digestformatbuf[digest_unorm_t::max_natural_width_digits + 9]; |
|||
sprintf(digestformatbuf, "%%-%us %%s\n", |
|||
static_cast<unsigned>(digest_unorm_t::max_natural_width)); |
|||
#if 0 |
|||
fprintf(stderr, "FORMAT STRING: %s\n", digestformatbuf); |
|||
#endif |
|||
|
|||
// iterate over all files given
|
|||
for (int i = 1; i < argc; i++) |
|||
{ |
|||
char* filename = argv[i]; |
|||
FILE* fp = fopen(filename, "rb"); |
|||
|
|||
// error when failed to open file
|
|||
if (!fp) |
|||
{ |
|||
perror(filename); |
|||
return 1; |
|||
} |
|||
|
|||
#ifdef OPTIMIZE_CONSTANT_FILE_SIZE |
|||
/*
|
|||
Retrieve file size (but the file is not seekable, do nothing). |
|||
Note that using off_t is not safe for 32-bit platform. |
|||
Use something much more robust to retrieve file sizes. |
|||
*/ |
|||
bool seekable = false; |
|||
off_t filesize; |
|||
if (fseek(fp, 0, SEEK_END) == 0) |
|||
{ |
|||
/*
|
|||
If seekable, set file size constant for |
|||
digest generator optimization. The generator will work without it |
|||
but using it makes the program significantly faster. |
|||
*/ |
|||
seekable = true; |
|||
filesize = ftello(fp); |
|||
if (fseek(fp, 0, SEEK_SET) != 0) |
|||
{ |
|||
fprintf(stderr, "%s: could not seek to the beginning.\n", filename); |
|||
fclose(fp); |
|||
return 1; |
|||
} |
|||
/*
|
|||
set_file_size_constant fails if: |
|||
* set_file_size_constant is called multiple times (not happens here) |
|||
* given file size is too large to optimize |
|||
*/ |
|||
if (!gen.set_file_size_constant(filesize)) |
|||
{ |
|||
fprintf(stderr, "%s: cannot optimize performance for this file.\n", filename); |
|||
fclose(fp); |
|||
return 1; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
#if 0 |
|||
fprintf(stderr, "%s: seek operation is not available.\n", filename); |
|||
#endif |
|||
} |
|||
#endif |
|||
|
|||
// update generator by given file stream and buffer
|
|||
if (!gen.update_by_stream<BUFFER_SIZE>(fp, filebuf)) |
|||
{ |
|||
fprintf(stderr, "%s: failed to update fuzzy hashes.\n", filename); |
|||
fclose(fp); |
|||
return 1; |
|||
} |
|||
fclose(fp); |
|||
|
|||
/*
|
|||
copy_digest will fail if: |
|||
* The file was too big to process |
|||
* The file size did not match with file size constant |
|||
(set by set_file_size_constant) |
|||
*/ |
|||
if (!gen.copy_digest(d)) |
|||
{ |
|||
if (gen.is_total_size_clamped()) |
|||
fprintf(stderr, "%s: too big to process.\n", filename); |
|||
#ifdef OPTIMIZE_CONSTANT_FILE_SIZE |
|||
else if (seekable && (digest_filesize_t(filesize) != gen.total_size())) |
|||
fprintf(stderr, "%s: file size changed while reading (or arithmetic overflow?)\n", filename); |
|||
#endif |
|||
else |
|||
fprintf(stderr, "%s: failed to copy digest with unknown error.\n", filename); |
|||
return 1; |
|||
} |
|||
|
|||
/*
|
|||
If size of the digest buffer is equal or greater than max_natural_chars, |
|||
copying the string digest with pretty_unsafe function is completely safe. |
|||
|
|||
However, this function still may fail due to failure of sprintf. |
|||
*/ |
|||
if (!d.pretty_unsafe(digestbuf)) |
|||
{ |
|||
fprintf(stderr, "%s: failed to stringize the digest.\n", filename); |
|||
return 1; |
|||
} |
|||
printf(digestformatbuf, digestbuf, filename); |
|||
|
|||
// reset the generator if we haven't reached the end
|
|||
if (i + 1 != argc) |
|||
gen.reset(); |
|||
} |
|||
return 0; |
|||
} |
@ -0,0 +1,2 @@ |
|||
* |
|||
!/.gitignore |
@ -0,0 +1,76 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
ffuzzy.hpp |
|||
Fuzzy hashing implementation |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_ROOT_FFUZZY_HPP |
|||
#define FFUZZYPP_ROOT_FFUZZY_HPP |
|||
|
|||
#include "ffuzzypp/utils/likely.hpp" |
|||
#include "ffuzzypp/utils/minmax.hpp" |
|||
#include "ffuzzypp/utils/safe_int.hpp" |
|||
#include "ffuzzypp/utils/static_assert_query.hpp" |
|||
#include "ffuzzypp/utils/numeric_digits.hpp" |
|||
#include "ffuzzypp/utils/type_modifier.hpp" |
|||
#include "ffuzzypp/utils/ranges.hpp" |
|||
#include "ffuzzypp/base64.hpp" |
|||
#include "ffuzzypp/context_hash.hpp" |
|||
#include "ffuzzypp/context_hash_fast.hpp" |
|||
#include "ffuzzypp/rolling_hash.hpp" |
|||
#include "ffuzzypp/rolling_hash_ssdeep.hpp" |
|||
#include "ffuzzypp/strings/position_array.hpp" |
|||
#include "ffuzzypp/strings/common_substr.hpp" |
|||
#include "ffuzzypp/strings/edit_dist.hpp" |
|||
#include "ffuzzypp/strings/terminators.hpp" |
|||
#include "ffuzzypp/strings/transform.hpp" |
|||
#include "ffuzzypp/strings/sequences.hpp" |
|||
#include "ffuzzypp/strings/nosequences.hpp" |
|||
#include "ffuzzypp/digest_blocksize.hpp" |
|||
#include "ffuzzypp/digest_data.hpp" |
|||
#include "ffuzzypp/digest_position_array_base.hpp" |
|||
#include "ffuzzypp/digest_comparison.hpp" |
|||
#include "ffuzzypp/digest_base.hpp" |
|||
#include "ffuzzypp/digest_position_array.hpp" |
|||
#include "ffuzzypp/digest.hpp" |
|||
#include "ffuzzypp/digest_filesize.hpp" |
|||
#include "ffuzzypp/digest_generator.hpp" |
|||
|
|||
#ifdef FFUZZYPP_COMPATIBILITY_SSDEEP_2_9 |
|||
#error Configuration by FFUZZYPP_COMPATIBILITY_SSDEEP_2_9 is now removed. Read README for alternative method. |
|||
#endif |
|||
#ifdef FFUZZYPP_COMPATIBILITY_SSDEEP |
|||
static_assert(ffuzzy::digest_params::max_blockhash_len == 64, |
|||
"Given parameter (digest_params::max_blockhash_len) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_params::max_blockhash_sequence == 3, |
|||
"Given parameter (digest_params::max_blockhash_sequence) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::blockhash_comparison_params::min_match_len == 7, |
|||
"Given parameter (blockhash_comparison_params::min_match_len) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::rolling_hash::window_size == 7, |
|||
"Given parameter (rolling_hash::window_size) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_blocksize::number_of_blockhashes == 31, |
|||
"Given parameter (digest_blocksize::number_of_blockhashes) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_blocksize::min_blocksize == 3, |
|||
"Given parameter (digest_blocksize::min_blocksize) is not compatible with ssdeep."); |
|||
static_assert(ffuzzy::digest_filesize::min_supported_size == 4097, |
|||
"Given parameter (digest_filesize::min_supported_size) is not compatible with ssdeep."); |
|||
#endif |
|||
|
|||
#endif |
@ -0,0 +1,36 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
ffuzzy_declarations.cpp |
|||
Your linker's friend |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#define FFUZZYPP_DECLARATIONS |
|||
#include "ffuzzy.hpp" |
|||
|
|||
extern "C" { |
|||
const char* ffuzzy_declarations_version(void) |
|||
{ |
|||
#ifdef PACKAGE_VERSION |
|||
return PACKAGE_VERSION; |
|||
#else |
|||
return "unknown"; |
|||
#endif |
|||
} |
|||
} |
@ -0,0 +1,196 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
base64.hpp |
|||
Base64 utilities |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_BASE64_HPP |
|||
#define FFUZZYPP_BASE64_HPP |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class base64 |
|||
{ |
|||
private: |
|||
base64(void) = delete; |
|||
base64(const base64&) = delete; |
|||
|
|||
// Base64 table
|
|||
public: |
|||
static constexpr const char invalid_index = 64; |
|||
static constexpr const char values[64] = |
|||
{ |
|||
// This should be the same order of ordinal Base64
|
|||
// (as well as b64 variable in fuzzy.c)
|
|||
'A', 'B', 'C', 'D', 'E', 'F', 'G', |
|||
'H', 'I', 'J', 'K', 'L', 'M', 'N', |
|||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
|||
'V', 'W', 'X', 'Y', 'Z', |
|||
'a', 'b', 'c', 'd', 'e', 'f', 'g', |
|||
'h', 'i', 'j', 'k', 'l', 'm', 'n', |
|||
'o', 'p', 'q', 'r', 's', 't', 'u', |
|||
'v', 'w', 'x', 'y', 'z', |
|||
'0', '1', '2', '3', '4', |
|||
'5', '6', '7', '8', '9', |
|||
'+', '/' |
|||
}; |
|||
static_assert( |
|||
sizeof(values) == 64 && |
|||
values[ 0] == 'A' && |
|||
values[26] == 'a' && |
|||
values[52] == '0' && |
|||
values[62] == '+' && |
|||
values[63] == '/', |
|||
"minimum sanity check for Base64 failed."); |
|||
static_assert( |
|||
invalid_index < 0 || invalid_index >= 64, |
|||
"invalid_index is not out of range."); |
|||
|
|||
public: |
|||
// Utility to check whether given character is in Base64 charset
|
|||
static bool isbase64(char c) noexcept |
|||
{ |
|||
switch (c) |
|||
{ |
|||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': |
|||
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': |
|||
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': |
|||
case 'V': case 'W': case 'X': case 'Y': case 'Z': |
|||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': |
|||
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': |
|||
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': |
|||
case 'v': case 'w': case 'x': case 'y': case 'z': |
|||
case '0': case '1': case '2': case '3': case '4': |
|||
case '5': case '6': case '7': case '8': case '9': |
|||
case '+': case '/': |
|||
return true; |
|||
default: |
|||
return false; |
|||
} |
|||
} |
|||
// Utility to convert character to Base64 index
|
|||
static char toindex(char c) noexcept |
|||
{ |
|||
switch (c) |
|||
{ |
|||
case 'A': return 0; |
|||
case 'B': return 1; |
|||
case 'C': return 2; |
|||
case 'D': return 3; |
|||
case 'E': return 4; |
|||
case 'F': return 5; |
|||
case 'G': return 6; |
|||
case 'H': return 7; |
|||
case 'I': return 8; |
|||
case 'J': return 9; |
|||
case 'K': return 10; |
|||
case 'L': return 11; |
|||
case 'M': return 12; |
|||
case 'N': return 13; |
|||
case 'O': return 14; |
|||
case 'P': return 15; |
|||
case 'Q': return 16; |
|||
case 'R': return 17; |
|||
case 'S': return 18; |
|||
case 'T': return 19; |
|||
case 'U': return 20; |
|||
case 'V': return 21; |
|||
case 'W': return 22; |
|||
case 'X': return 23; |
|||
case 'Y': return 24; |
|||
case 'Z': return 25; |
|||
case 'a': return 26; |
|||
case 'b': return 27; |
|||
case 'c': return 28; |
|||
case 'd': return 29; |
|||
case 'e': return 30; |
|||
case 'f': return 31; |
|||
case 'g': return 32; |
|||
case 'h': return 33; |
|||
case 'i': return 34; |
|||
case 'j': return 35; |
|||
case 'k': return 36; |
|||
case 'l': return 37; |
|||
case 'm': return 38; |
|||
case 'n': return 39; |
|||
case 'o': return 40; |
|||
case 'p': return 41; |
|||
case 'q': return 42; |
|||
case 'r': return 43; |
|||
case 's': return 44; |
|||
case 't': return 45; |
|||
case 'u': return 46; |
|||
case 'v': return 47; |
|||
case 'w': return 48; |
|||
case 'x': return 49; |
|||
case 'y': return 50; |
|||
case 'z': return 51; |
|||
case '0': return 52; |
|||
case '1': return 53; |
|||
case '2': return 54; |
|||
case '3': return 55; |
|||
case '4': return 56; |
|||
case '5': return 57; |
|||
case '6': return 58; |
|||
case '7': return 59; |
|||
case '8': return 60; |
|||
case '9': return 61; |
|||
case '+': return 62; |
|||
case '/': return 63; |
|||
default: return invalid_index; |
|||
} |
|||
} |
|||
|
|||
// Base64 transformation (from index to actual character)
|
|||
public: |
|||
class transform_to_b64 |
|||
{ |
|||
private: |
|||
transform_to_b64(void) = delete; |
|||
transform_to_b64(const transform_to_b64&) = delete; |
|||
public: |
|||
static constexpr char transform(char ch) noexcept |
|||
{ |
|||
return base64::values[static_cast<unsigned char>(ch)]; |
|||
} |
|||
}; |
|||
|
|||
// Base64 reverse-transformation (from Base64 character to index)
|
|||
public: |
|||
class transform_from_b64 |
|||
{ |
|||
private: |
|||
transform_from_b64(void) = delete; |
|||
transform_from_b64(const transform_from_b64&) = delete; |
|||
public: |
|||
static char transform(char ch) noexcept |
|||
{ |
|||
return toindex(ch); |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
#ifdef FFUZZYPP_DECLARATIONS |
|||
constexpr const char base64::values[64]; |
|||
#endif |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
context_hash.hpp |
|||
Context computation (non-rolling) hash implementation |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_CONTEXT_HASH_HPP |
|||
#define FFUZZYPP_CONTEXT_HASH_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class context_hash |
|||
{ |
|||
private: |
|||
// Don't use UINT32_C not to define __STDC_CONSTANT_MACROS in ffuzzy++
|
|||
static constexpr const uint_least32_t hash_init = uint_least32_t(0x28021967ul); |
|||
static constexpr const uint_least32_t hash_prime = uint_least32_t(0x01000193ul); |
|||
private: |
|||
uint_least32_t h; |
|||
public: |
|||
static constexpr uint_least32_t initial_state(void) noexcept |
|||
{ |
|||
return hash_init; |
|||
} |
|||
void reset(void) noexcept |
|||
{ |
|||
h = initial_state(); |
|||
} |
|||
static constexpr uint_least32_t next_state(uint_least32_t h, unsigned char c) noexcept |
|||
{ |
|||
return ((h * hash_prime) ^ uint_least32_t(c)) & uint_least32_t(0xfffffffful); |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
h = next_state(h, c); |
|||
} |
|||
uint_least32_t sum(void) const noexcept |
|||
{ |
|||
return h; |
|||
} |
|||
public: |
|||
char sum_in_base64(void) const noexcept |
|||
{ |
|||
// sum for Base64 (returns Base64 index)
|
|||
return static_cast<char>(h & 0x3f); |
|||
} |
|||
public: |
|||
context_hash(void) noexcept = default; // initialize to undefined state
|
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,480 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
context_hash_fast.hpp |
|||
Fast implementation of context_hash (only lowest 6-bits) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_CONTEXT_HASH_FAST_HPP |
|||
#define FFUZZYPP_CONTEXT_HASH_FAST_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
#include "context_hash.hpp" |
|||
#include "utils/static_assert_query.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class context_hash_fast |
|||
{ |
|||
private: |
|||
static constexpr const char hash_init = static_cast<char>(context_hash::initial_state() & 0x3f); |
|||
static constexpr const char table_translate[64][64] = |
|||
{ |
|||
{ // 0x00
|
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
}, |
|||
{ // 0x01
|
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
}, |
|||
{ // 0x02
|
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
}, |
|||
{ // 0x03
|
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
}, |
|||
{ // 0x04
|
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
}, |
|||
{ // 0x05
|
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
}, |
|||
{ // 0x06
|
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
}, |
|||
{ // 0x07
|
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
}, |
|||
{ // 0x08
|
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
}, |
|||
{ // 0x09
|
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
}, |
|||
{ // 0x0a
|
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
}, |
|||
{ // 0x0b
|
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
}, |
|||
{ // 0x0c
|
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
}, |
|||
{ // 0x0d
|
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
}, |
|||
{ // 0x0e
|
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
}, |
|||
{ // 0x0f
|
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
}, |
|||
{ // 0x10
|
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
}, |
|||
{ // 0x11
|
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
}, |
|||
{ // 0x12
|
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
}, |
|||
{ // 0x13
|
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
}, |
|||
{ // 0x14
|
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
}, |
|||
{ // 0x15
|
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
}, |
|||
{ // 0x16
|
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
}, |
|||
{ // 0x17
|
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
}, |
|||
{ // 0x18
|
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
}, |
|||
{ // 0x19
|
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
}, |
|||
{ // 0x1a
|
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
}, |
|||
{ // 0x1b
|
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
}, |
|||
{ // 0x1c
|
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
}, |
|||
{ // 0x1d
|
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
}, |
|||
{ // 0x1e
|
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
}, |
|||
{ // 0x1f
|
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
}, |
|||
{ // 0x20
|
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
}, |
|||
{ // 0x21
|
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
}, |
|||
{ // 0x22
|
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
}, |
|||
{ // 0x23
|
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
}, |
|||
{ // 0x24
|
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
}, |
|||
{ // 0x25
|
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
}, |
|||
{ // 0x26
|
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
}, |
|||
{ // 0x27
|
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
}, |
|||
{ // 0x28
|
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
}, |
|||
{ // 0x29
|
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
}, |
|||
{ // 0x2a
|
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
}, |
|||
{ // 0x2b
|
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
}, |
|||
{ // 0x2c
|
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
}, |
|||
{ // 0x2d
|
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
}, |
|||
{ // 0x2e
|
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
}, |
|||
{ // 0x2f
|
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
}, |
|||
{ // 0x30
|
|||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
|||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
|||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
|||
}, |
|||
{ // 0x31
|
|||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, |
|||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, |
|||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, |
|||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, |
|||
}, |
|||
{ // 0x32
|
|||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, |
|||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, |
|||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, |
|||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, |
|||
}, |
|||
{ // 0x33
|
|||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, |
|||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, |
|||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, |
|||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, |
|||
}, |
|||
{ // 0x34
|
|||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, |
|||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, |
|||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, |
|||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, |
|||
}, |
|||
{ // 0x35
|
|||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, |
|||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, |
|||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, |
|||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, |
|||
}, |
|||
{ // 0x36
|
|||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, |
|||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, |
|||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, |
|||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, |
|||
}, |
|||
{ // 0x37
|
|||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, |
|||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, |
|||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, |
|||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, |
|||
}, |
|||
{ // 0x38
|
|||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
|||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
|||
}, |
|||
{ // 0x39
|
|||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, |
|||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, |
|||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, |
|||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, |
|||
}, |
|||
{ // 0x3a
|
|||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, |
|||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, |
|||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, |
|||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, |
|||
}, |
|||
{ // 0x3b
|
|||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, |
|||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, |
|||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, |
|||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, |
|||
}, |
|||
{ // 0x3c
|
|||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, |
|||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, |
|||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, |
|||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, |
|||
}, |
|||
{ // 0x3d
|
|||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
|||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, |
|||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, |
|||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, |
|||
}, |
|||
{ // 0x3e
|
|||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, |
|||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, |
|||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, |
|||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, |
|||
}, |
|||
{ // 0x3f
|
|||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, |
|||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, |
|||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, |
|||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, |
|||
}, |
|||
}; |
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
template <uintmax_t i> |
|||
struct translate_check |
|||
{ |
|||
template <uintmax_t j> |
|||
struct translate_check_2 |
|||
{ |
|||
static constexpr const bool value = |
|||
table_translate[i][j] == (context_hash::next_state(i, j) & 0x3f); |
|||
}; |
|||
static constexpr const bool value = |
|||
static_assert_query::is_all<translate_check_2, 64>::value; |
|||
}; |
|||
static_assert(static_assert_query::is_all<translate_check, 64>::value, |
|||
"table_translate is not equivalent to actual context_hash translation."); |
|||
#endif |
|||
|
|||
private: |
|||
char h; |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
h = hash_init; |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
h = table_translate[static_cast<unsigned char>(h)][c & 0x3f]; |
|||
} |
|||
char sum_in_base64(void) const noexcept |
|||
{ |
|||
// sum for Base64 (returns Base64 index)
|
|||
return h; |
|||
} |
|||
public: |
|||
context_hash_fast(void) noexcept = default; // initialize to undefined state
|
|||
}; |
|||
|
|||
|
|||
#ifdef FFUZZYPP_DECLARATIONS |
|||
constexpr const char context_hash_fast::table_translate[64][64]; |
|||
#endif |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,495 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest.hpp |
|||
Fuzzy digest (wrapper with converters) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_HPP |
|||
#define FFUZZYPP_DIGEST_HPP |
|||
|
|||
#include <algorithm> |
|||
#include <functional> |
|||
#include <string> |
|||
#include <type_traits> |
|||
|
|||
#include "digest_data.hpp" |
|||
#include "digest_base.hpp" |
|||
#include "utils/type_modifier.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
// Wrapper class
|
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> class digest; |
|||
|
|||
// Digest (alphabet restricted; short; normalized)
|
|||
template <> |
|||
class digest<true, true, true> |
|||
: public digest_base<true, true, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, true, true>(other) {} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, true, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, true, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet restricted; short; unnormalized)
|
|||
template <> |
|||
class digest<true, true, false> |
|||
: public digest_base<true, true, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, true, false>(other) {} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
digest_data<true, true>::operator=(other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, true, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, true, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
digest_data<true, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet restricted; long; normalized)
|
|||
template <> |
|||
class digest<true, false, true> |
|||
: public digest_base<true, false, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, false, true>(other) {} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, false, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, false, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet restricted; long; unnormalized)
|
|||
template <> |
|||
class digest<true, false, false> |
|||
: public digest_base<true, false, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<true, false, false>(other) {} |
|||
digest(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
digest_data<true, false>::operator=(other); |
|||
} |
|||
digest(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<true, false, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<true, false, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
digest_data<true, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; short; normalized)
|
|||
template <> |
|||
class digest<false, true, true> |
|||
: public digest_base<false, true, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, true, true>(other) {} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, true, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, true, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; short; unnormalized)
|
|||
template <> |
|||
class digest<false, true, false> |
|||
: public digest_base<false, true, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, true, false>(other) {} |
|||
digest(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
digest_data<false, true>::operator=(other); |
|||
} |
|||
digest(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, true, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, true, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
digest_data<false, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; long; normalized)
|
|||
template <> |
|||
class digest<false, false, true> |
|||
: public digest_base<false, false, true> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, false, true>(other) {} |
|||
digest(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, false, true>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, false, true>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Digest (alphabet not restricted; long; unnormalized)
|
|||
template <> |
|||
class digest<false, false, false> |
|||
: public digest_base<false, false, false> |
|||
{ |
|||
public: |
|||
digest(void) noexcept = default; // initialize to undefined state
|
|||
digest(const digest& other) noexcept : digest_base<false, false, false>(other) {} |
|||
digest(const digest<false, false, true>& other) noexcept |
|||
{ |
|||
digest_data<false, false>::operator=(other); |
|||
} |
|||
digest(const digest<false, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
} |
|||
digest(const digest<true, false, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
} |
|||
digest(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
} |
|||
explicit digest(const char* str) noexcept(false) : digest_base<false, false, false>(str) {} |
|||
explicit digest(const std::string& str) : digest(str.c_str()) {} |
|||
const digest& operator=(const digest& other) noexcept |
|||
{ |
|||
digest_base<false, false, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, false, true>& other) noexcept |
|||
{ |
|||
digest_data<false, false>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<false, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, false, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, false>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
const digest& operator=(const digest<true, true, true>& other) noexcept |
|||
{ |
|||
internal::digest_copy::copy_to_long_non_ra(*this, other); |
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
// Typedefs for digest specializations
|
|||
typedef digest<false, true, true> digest_t; |
|||
typedef digest<false, true, false> digest_unorm_t; |
|||
typedef digest<false, false, true> digest_long_t; |
|||
typedef digest<false, false, false> digest_long_unorm_t; |
|||
typedef digest< true, true, true> digest_ra_t; |
|||
typedef digest< true, true, false> digest_ra_unorm_t; |
|||
typedef digest< true, false, true> digest_ra_long_t; |
|||
typedef digest< true, false, false> digest_ra_long_unorm_t; |
|||
|
|||
|
|||
|
|||
/*
|
|||
Type modifiers |
|||
*/ |
|||
namespace internal |
|||
{ |
|||
template <typename T> |
|||
struct digest_traits |
|||
{ |
|||
static constexpr const bool is_valid = false; |
|||
}; |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
struct digest_traits<digest_base<IsAlphabetRestricted, IsShort, IsNormalized>> |
|||
{ |
|||
static constexpr const bool is_valid = true; |
|||
typedef digest_base<IsAlphabetRestricted, IsShort, true> norm_type; |
|||
typedef digest_base<IsAlphabetRestricted, IsShort, false> unorm_type; |
|||
typedef digest_base<IsAlphabetRestricted, true, IsNormalized> short_type; |
|||
typedef digest_base<IsAlphabetRestricted, false, IsNormalized> long_type; |
|||
typedef digest_base< true, IsShort, IsNormalized> ra_type; |
|||
typedef digest_base<false, IsShort, IsNormalized> non_ra_type; |
|||
}; |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
struct digest_traits<digest<IsAlphabetRestricted, IsShort, IsNormalized>> |
|||
{ |
|||
static constexpr const bool is_valid = true; |
|||
typedef digest<IsAlphabetRestricted, IsShort, true> norm_type; |
|||
typedef digest<IsAlphabetRestricted, IsShort, false> unorm_type; |
|||
typedef digest<IsAlphabetRestricted, true, IsNormalized> short_type; |
|||
typedef digest<IsAlphabetRestricted, false, IsNormalized> long_type; |
|||
typedef digest< true, IsShort, IsNormalized> ra_type; |
|||
typedef digest<false, IsShort, IsNormalized> non_ra_type; |
|||
}; |
|||
template <typename T> |
|||
struct digest_alt_type_selector |
|||
{ |
|||
private: |
|||
typedef digest_traits<typename std::remove_cv<T>::type> traits_type; |
|||
static_assert(traits_type::is_valid, "You must give correct type to retrieve alternative types."); |
|||
public: |
|||
typedef typename type_mod::cv_match<T, typename traits_type::norm_type>::type norm_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::unorm_type>::type unorm_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::short_type>::type short_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::long_type>::type long_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::ra_type>::type ra_type; |
|||
typedef typename type_mod::cv_match<T, typename traits_type::non_ra_type>::type non_ra_type; |
|||
}; |
|||
} |
|||
template <typename T> using digest_to_unorm = typename internal::digest_alt_type_selector<T>::unorm_type; |
|||
template <typename T> using digest_to_norm = typename internal::digest_alt_type_selector<T>::norm_type; |
|||
template <typename T> using digest_to_short = typename internal::digest_alt_type_selector<T>::short_type; |
|||
template <typename T> using digest_to_long = typename internal::digest_alt_type_selector<T>::long_type; |
|||
template <typename T> using digest_to_ra = typename internal::digest_alt_type_selector<T>::ra_type; |
|||
template <typename T> using digest_to_non_ra = typename internal::digest_alt_type_selector<T>::non_ra_type; |
|||
|
|||
|
|||
/*
|
|||
We expect following properties for each digest types: |
|||
|
|||
* is a trivially default constructible type |
|||
* is a standard-layout type |
|||
|
|||
Digest types are: |
|||
|
|||
* digest_data<IsAlphabetRestricted, IsShort> |
|||
* digest<IsAlphabetRestricted, IsShort, true> |
|||
* digest<IsAlphabetRestricted, IsShort, false> |
|||
|
|||
We also expect that these types are nearly equivalent. |
|||
*/ |
|||
#ifdef FFUZZYPP_LOCAL_CHK |
|||
#error do not define FFUZZYPP_LOCAL_CHK |
|||
#endif |
|||
#ifdef FFUZZYPP_LOCAL_CHK1 |
|||
#error do not define FFUZZYPP_LOCAL_CHK1 |
|||
#endif |
|||
#define FFUZZYPP_LOCAL_CHK1(IsAlphabetRestricted, IsShort, IsNormalized) \ |
|||
static_assert(std::is_trivially_default_constructible<digest<IsAlphabetRestricted, IsShort, IsNormalized>>::value, \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", " #IsNormalized "> must be a trivially default constructible type."); \ |
|||
static_assert(std::is_standard_layout<digest<IsAlphabetRestricted, IsShort, IsNormalized>>::value, \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", " #IsNormalized "> must be a standard-layout type."); \ |
|||
static_assert(std::is_base_of<digest_data<IsAlphabetRestricted, IsShort>, digest<IsAlphabetRestricted, IsShort, IsNormalized>>::value, \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort ">, digest<" #IsAlphabetRestricted ", " #IsShort ", true> and " \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", false> must be nearly equivalent."); \ |
|||
static_assert(sizeof(digest_data<IsAlphabetRestricted, IsShort>) == sizeof(digest<IsAlphabetRestricted, IsShort, IsNormalized>), \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort ">, digest<" #IsAlphabetRestricted ", " #IsShort ", true> and " \ |
|||
"digest<" #IsAlphabetRestricted ", " #IsShort ", false> must be nearly equivalent.") |
|||
#define FFUZZYPP_LOCAL_CHK(IsAlphabetRestricted, IsShort) \ |
|||
static_assert(std::is_trivially_default_constructible<digest_data<IsAlphabetRestricted, IsShort>>::value, \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort "> must be a trivially default constructible type."); \ |
|||
static_assert(std::is_standard_layout<digest_data<IsAlphabetRestricted, IsShort>>::value, \ |
|||
"digest_data<" #IsAlphabetRestricted ", " #IsShort "> must be a standard-layout type."); \ |
|||
FFUZZYPP_LOCAL_CHK1(IsAlphabetRestricted, IsShort, true); \ |
|||
FFUZZYPP_LOCAL_CHK1(IsAlphabetRestricted, IsShort, false) |
|||
FFUZZYPP_LOCAL_CHK(true, true); |
|||
FFUZZYPP_LOCAL_CHK(true, false); |
|||
FFUZZYPP_LOCAL_CHK(false, true); |
|||
FFUZZYPP_LOCAL_CHK(false, false); |
|||
#undef FFUZZYPP_LOCAL_CHK |
|||
#undef FFUZZYPP_LOCAL_CHK1 |
|||
|
|||
} |
|||
|
|||
|
|||
// Specialization of standard hash and swap
|
|||
namespace std |
|||
{ |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
struct hash<ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>> |
|||
{ |
|||
size_t operator()(const ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>& value) const |
|||
{ |
|||
return value.hash(); |
|||
} |
|||
}; |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> |
|||
inline void swap( |
|||
ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>& a, |
|||
ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>& b |
|||
) noexcept |
|||
{ |
|||
ffuzzy::digest<IsAlphabetRestricted, IsShort, IsNormalized>::swap(a, b); |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,254 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_base.hpp |
|||
Fuzzy digest (wrapper with comparison methods) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_BASE_HPP |
|||
#define FFUZZYPP_DIGEST_BASE_HPP |
|||
|
|||
#include <string> |
|||
|
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "digest_comparison.hpp" |
|||
#include "utils/type_modifier.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
template <bool IsAlphabetRestricted, bool IsShort, bool IsNormalized> class digest_base; |
|||
|
|||
// Normalized form of digest (with specialized/fast comparison)
|
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
class digest_base<IsAlphabetRestricted, IsShort, true> |
|||
: public digest_data<IsAlphabetRestricted, IsShort> |
|||
{ |
|||
public: |
|||
digest_base(void) noexcept = default; // initialize to undefined state
|
|||
digest_base(const digest_base& other) noexcept : digest_data<IsAlphabetRestricted, IsShort>(other) {} |
|||
const digest_base& operator=(const digest_base& other) noexcept |
|||
{ |
|||
digest_data<IsAlphabetRestricted, IsShort>::operator=(other); |
|||
return *this; |
|||
} |
|||
public: |
|||
explicit digest_base(const char* str) noexcept(false) |
|||
{ |
|||
if (!digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(*this, str)) |
|||
throw digest_parse_error(); |
|||
} |
|||
explicit digest_base(const std::string& str) |
|||
: digest_base(str.c_str()) {} |
|||
static bool parse_normalized(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(digest, str); |
|||
} |
|||
static bool parse(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(digest, str); |
|||
} |
|||
public: |
|||
bool is_valid(void) const noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::is_valid() && this->template is_normalized(); |
|||
} |
|||
|
|||
// Comparison
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_identical( |
|||
const digest_base& value |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_identical(value); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base& other) const noexcept |
|||
{ |
|||
return compare<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_identical(void) const noexcept |
|||
{ |
|||
return compare_identical<Version>(*this); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_eq<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_lt(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(*this, other); |
|||
} |
|||
|
|||
// Comparison (on different digests)
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_diff(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_diff(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq_diff(const digest_base& other) const noexcept |
|||
{ |
|||
return compare_near_eq_diff<Version>(*this, other); |
|||
} |
|||
}; |
|||
|
|||
// Unnormalized form of digest (with normalization ability and slow comparison)
|
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
class digest_base<IsAlphabetRestricted, IsShort, false> |
|||
: public digest_data<IsAlphabetRestricted, IsShort> |
|||
{ |
|||
public: |
|||
digest_base(void) noexcept = default; // initialize to undefined state
|
|||
digest_base(const digest_base& other) noexcept : digest_data<IsAlphabetRestricted, IsShort>(other) {} |
|||
digest_base(const digest_base<IsAlphabetRestricted, IsShort, true>& other) noexcept : digest_data<IsAlphabetRestricted, IsShort>(other) {} |
|||
const digest_base& operator=(const digest_base& other) noexcept |
|||
{ |
|||
digest_data<IsAlphabetRestricted, IsShort>::operator=(other); |
|||
return *this; |
|||
} |
|||
const digest_base& operator=(const digest_base<IsAlphabetRestricted, IsShort, true>& other) noexcept |
|||
{ |
|||
digest_data<IsAlphabetRestricted, IsShort>::operator=(other); |
|||
return *this; |
|||
} |
|||
public: |
|||
explicit digest_base(const char* str) noexcept(false) |
|||
{ |
|||
if (!digest_data<IsAlphabetRestricted, IsShort>::parse(*this, str)) |
|||
throw digest_parse_error(); |
|||
} |
|||
explicit digest_base(const std::string& str) |
|||
: digest_base(str.c_str()) {} |
|||
static bool parse_normalized(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse_normalized(digest, str); |
|||
} |
|||
static bool parse(digest_base& digest, const char* str) noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>::parse(digest, str); |
|||
} |
|||
public: |
|||
digest_base<IsAlphabetRestricted, IsShort, true> to_normalized(void) const noexcept |
|||
{ |
|||
return digest_data<IsAlphabetRestricted, IsShort>:: |
|||
template normalize<digest_base<IsAlphabetRestricted, IsShort, true>>(*this); |
|||
} |
|||
explicit operator digest_base<IsAlphabetRestricted, IsShort, true>(void) const noexcept { return to_normalized(); } |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base& a, |
|||
const digest_base& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_unnormalized(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base& other) const noexcept { return compare<Version>(*this, other); } |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,255 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_blocksize.hpp |
|||
Fuzzy digest block size utilities |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_BLOCKSIZE_HPP |
|||
#define FFUZZYPP_DIGEST_BLOCKSIZE_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include <limits> |
|||
|
|||
#include "utils/safe_int.hpp" |
|||
#include "utils/static_assert_query.hpp" |
|||
#include "utils/numeric_digits.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
// Block size type for fuzzy digest
|
|||
typedef uint_least32_t digest_blocksize_t; |
|||
|
|||
// Block size utilities
|
|||
class digest_blocksize |
|||
{ |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<digest_blocksize_t>() && |
|||
std::numeric_limits<digest_blocksize_t>::max() >= 0xfffffffful, |
|||
"digest_blocksize_t must be an unsigned integral type which can represent all 32-bit values." |
|||
); |
|||
private: |
|||
digest_blocksize(void) = delete; |
|||
digest_blocksize(const digest_blocksize&) = delete; |
|||
|
|||
// Parameters
|
|||
public: |
|||
static constexpr const unsigned number_of_blockhashes = 31; |
|||
static constexpr const digest_blocksize_t min_blocksize = 3; |
|||
static constexpr const digest_blocksize_t max_blocksize = min_blocksize << (number_of_blockhashes - 1); |
|||
static_assert(min_blocksize != 0, "min_blocksize must not be zero."); |
|||
static_assert(number_of_blockhashes != 0, "number_of_blockhashes must not be zero."); |
|||
static_assert(number_of_blockhashes <= 32, "number_of_blockhashes must be less than 32."); |
|||
static_assert(min_blocksize <= 0xfffffffful, "min_blocksize must be less than 2^32."); |
|||
static_assert( |
|||
safe_int::safe_lshift< |
|||
safe_int::uvalue<digest_blocksize_t, min_blocksize>, |
|||
safe_int::uvalue<unsigned, number_of_blockhashes - 1> |
|||
>::is_valid && |
|||
(min_blocksize << (number_of_blockhashes - 1)) <= 0xfffffffful, |
|||
"(min_blocksize << (number_of_blockhashes - 1)) must be less than 2^32."); |
|||
|
|||
// Maximum characters required to represent all "natural" block sizes
|
|||
// (excluding '\0' character at the end)
|
|||
public: |
|||
static constexpr const size_t max_natural_digits = |
|||
numeric_digits::in_decimal(max_blocksize); |
|||
|
|||
// Block size naturality
|
|||
// (whether this is possibly generated by ssdeep)
|
|||
public: |
|||
static bool is_natural(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
if (blocksize < min_blocksize) |
|||
return false; |
|||
if (blocksize > max_blocksize) |
|||
return false; |
|||
while (blocksize != min_blocksize) |
|||
{ |
|||
if (blocksize % 2 != 0) |
|||
return false; |
|||
blocksize /= 2; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Utility to prevent arithmetic overflow
|
|||
public: |
|||
static constexpr bool is_safe_to_double(digest_blocksize_t value) noexcept |
|||
{ |
|||
return value <= (std::numeric_limits<digest_blocksize_t>::max() / 2); |
|||
} |
|||
|
|||
/*
|
|||
"Near" relations |
|||
|
|||
fuzzy digests are compared against other if their block sizes are "near". |
|||
There are three such cases (if there is no arithmetic overflow). |
|||
|
|||
Case 1: a == b (eq) |
|||
Digest A : 3:xxxxxxx:yyyyyyy |
|||
Digest A : 3:xxxxxxx:yyyyyyy |
|||
| ~~~~~~~ ~~~~~~~ |
|||
| (both block hashes are compared and the maximum score is chosen) |
|||
Case 2: a * 2 == b (implies a < b; lt) |
|||
Digest A : 3:xxxxxxx:yyyyyyy |
|||
Digest B : 6: yyyyyyy:zzzzzzz |
|||
| ~~~~~~~ |
|||
| (block hash in common block size is compared) |
|||
Case 3: a == b * 2 (implies a > b; gt) |
|||
Digest A : 6: yyyyyyy:zzzzzzz |
|||
Digest B : 3:xxxxxxx:yyyyyyy |
|||
| ~~~~~~~ |
|||
| (block hash in common block size is compared) |
|||
*/ |
|||
public: |
|||
static constexpr bool is_near_eq(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return a == b; |
|||
} |
|||
static constexpr bool is_near_lt(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return a != b && is_safe_to_double(a) && a * 2 == b; |
|||
} |
|||
static constexpr bool is_near_gt(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return a != b && a % 2 == 0 && a / 2 == b; |
|||
} |
|||
static constexpr bool is_near_leq(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return is_near_eq(a, b) || is_near_lt(a, b); |
|||
} |
|||
static constexpr bool is_near(digest_blocksize_t a, digest_blocksize_t b) noexcept |
|||
{ |
|||
return is_near_eq(a, b) || is_near_lt(a, b) || is_near_gt(a, b); |
|||
} |
|||
|
|||
// Utility to convert block size index to a natural block size
|
|||
public: |
|||
static constexpr digest_blocksize_t at(unsigned index) noexcept |
|||
{ |
|||
return min_blocksize << index; |
|||
} |
|||
|
|||
// Utility to convert natural block size to corresponding block size index
|
|||
private: |
|||
template <bool UseMagic, typename Tdummy = void> class natural_to_index_impl; |
|||
// Implementation by magic table
|
|||
// (not general [depends on ssdeep parameters] but possibly fast)
|
|||
static constexpr const digest_blocksize_t nti_magic_mul = digest_blocksize_t(0x017713caul); |
|||
static constexpr const unsigned nti_magic_table[31] = |
|||
{ |
|||
0x00, 0x01, 0x02, 0x06, 0x03, 0x0b, 0x07, 0x10, |
|||
0x04, 0x0e, 0x0c, 0x18, 0x08, 0x15, 0x11, 0x1a, |
|||
0x1e, 0x05, 0x0a, 0x0f, 0x0d, 0x17, 0x14, 0x19, |
|||
0x1d, 0x09, 0x16, 0x13, 0x1c, 0x12, 0x1b, |
|||
}; |
|||
template <typename Tdummy> |
|||
class natural_to_index_impl<true, Tdummy> |
|||
{ |
|||
public: |
|||
static constexpr unsigned natural_to_index_unsafe(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return nti_magic_table[((blocksize * nti_magic_mul) >> 27) & 0x1f]; |
|||
} |
|||
private: |
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
template <uintmax_t i> |
|||
struct seq_check |
|||
{ |
|||
static constexpr const bool value = |
|||
natural_to_index_unsafe(min_blocksize << i) == i; |
|||
}; |
|||
static_assert(static_assert_query::is_all<seq_check, number_of_blockhashes>::value, |
|||
"magic table is not constructed correctly."); |
|||
#endif |
|||
}; |
|||
// Implementation by De Brujin sequence
|
|||
// (general but requires a division to make index_from work)
|
|||
static constexpr const digest_blocksize_t nti_debrujin_mul = digest_blocksize_t(0x077cb531ul); |
|||
static constexpr const unsigned nti_debrujin_table[32] = |
|||
{ |
|||
0x00, 0x01, 0x1c, 0x02, 0x1d, 0x0e, 0x18, 0x03, |
|||
0x1e, 0x16, 0x14, 0x0f, 0x19, 0x11, 0x04, 0x08, |
|||
0x1f, 0x1b, 0x0d, 0x17, 0x15, 0x13, 0x10, 0x07, |
|||
0x1a, 0x0c, 0x12, 0x06, 0x0b, 0x05, 0x0a, 0x09, |
|||
}; |
|||
template <typename Tdummy> |
|||
class natural_to_index_impl<false, Tdummy> |
|||
{ |
|||
public: |
|||
static constexpr unsigned natural_to_index_unsafe(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return nti_debrujin_table[((blocksize / min_blocksize * nti_debrujin_mul) >> 27) & 0x1f]; |
|||
} |
|||
private: |
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
template <uintmax_t i> |
|||
struct seq_check |
|||
{ |
|||
static constexpr const bool value = |
|||
natural_to_index_unsafe(min_blocksize << i) == i; |
|||
}; |
|||
static_assert(static_assert_query::is_all<seq_check, number_of_blockhashes>::value, |
|||
"De Brujin sequence is not constructed correctly."); |
|||
#endif |
|||
}; |
|||
static constexpr const bool nti_is_magic_available = |
|||
min_blocksize == 3 && number_of_blockhashes <= 31; |
|||
public: |
|||
template <bool UseDebrujin = false> |
|||
static constexpr unsigned natural_to_index_unsafe(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return natural_to_index_impl< |
|||
nti_is_magic_available && !UseDebrujin |
|||
>::natural_to_index_unsafe(blocksize); |
|||
} |
|||
template <bool UseDebrujin = false> |
|||
static unsigned natural_to_index(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_natural(blocksize)); |
|||
#endif |
|||
return natural_to_index_unsafe<UseDebrujin>(blocksize); |
|||
} |
|||
}; |
|||
|
|||
|
|||
#ifdef FFUZZYPP_DECLARATIONS |
|||
constexpr const unsigned digest_blocksize::nti_magic_table[31]; |
|||
constexpr const unsigned digest_blocksize::nti_debrujin_table[32]; |
|||
#endif |
|||
|
|||
} |
|||
|
|||
#endif |
File diff suppressed because it is too large
@ -0,0 +1,649 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_data.hpp |
|||
Fuzzy digest (data and basic portions) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_DATA_HPP |
|||
#define FFUZZYPP_DIGEST_DATA_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdlib> |
|||
#include <cstring> |
|||
|
|||
#include <functional> |
|||
#include <limits> |
|||
#include <string> |
|||
#include <type_traits> |
|||
#include <utility> |
|||
|
|||
#include <errno.h> |
|||
|
|||
#include "base64.hpp" |
|||
#include "digest_blocksize.hpp" |
|||
#include "strings/sequences.hpp" |
|||
#include "strings/nosequences.hpp" |
|||
#include "utils/safe_int.hpp" |
|||
#include "utils/numeric_digits.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
typedef size_t blockhash_len_t; |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<blockhash_len_t>(), |
|||
"blockhash_len_t must be an unsigned integral type."); |
|||
|
|||
struct digest_parse_error {}; |
|||
|
|||
|
|||
class digest_params |
|||
{ |
|||
private: |
|||
digest_params(void) = delete; |
|||
digest_params(const digest_params&) = delete; |
|||
|
|||
// Common digest parameters (for both short and long forms)
|
|||
public: |
|||
static constexpr const blockhash_len_t max_blockhash_len = 64; |
|||
static constexpr const blockhash_len_t max_blockhash_sequence = 3; |
|||
static_assert(max_blockhash_len >= 2, "max_blockhash_len must be 2 or greater."); |
|||
static_assert(max_blockhash_sequence != 0, "max_blockhash_sequence must not be zero."); |
|||
static_assert(safe_int::uvalue<size_t, max_blockhash_len>::is_valid, |
|||
"max_blockhash_len must be in range of size_t."); |
|||
static_assert(safe_int::uvalue<size_t, max_blockhash_sequence>::is_valid, |
|||
"max_blockhash_sequence must be in range of size_t."); |
|||
}; |
|||
|
|||
|
|||
template <bool IsAlphabetRestricted> class digest_data_transformation; |
|||
|
|||
template<> |
|||
class digest_data_transformation<true> |
|||
{ |
|||
private: |
|||
digest_data_transformation(void) = delete; |
|||
digest_data_transformation(const digest_data_transformation&) = delete; |
|||
public: |
|||
typedef base64::transform_from_b64 input_type; |
|||
typedef base64::transform_to_b64 output_type; |
|||
}; |
|||
|
|||
template<> |
|||
class digest_data_transformation<false> |
|||
{ |
|||
private: |
|||
digest_data_transformation(void) = delete; |
|||
digest_data_transformation(const digest_data_transformation&) = delete; |
|||
public: |
|||
typedef strings::default_char_transform input_type; |
|||
typedef strings::default_char_transform output_type; |
|||
}; |
|||
|
|||
|
|||
// Friend classes for digest_data class
|
|||
enum struct comparison_version; |
|||
namespace internal |
|||
{ |
|||
template <comparison_version> class digest_comparison_base; |
|||
class digest_copy; |
|||
} |
|||
template <comparison_version> class digest_comparison; |
|||
template <bool> class digest_position_array_base; |
|||
|
|||
|
|||
// Data structure for fuzzy digest (as base class)
|
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
class digest_data |
|||
{ |
|||
static_assert(digest_params::max_blockhash_len >= 4, |
|||
"max_blockhash_len must be at least 4 due to restrictions in this implementation."); |
|||
|
|||
// Maximum lengths for each block hashes
|
|||
public: |
|||
static constexpr const blockhash_len_t max_blockhash1_len = digest_params::max_blockhash_len; |
|||
static constexpr const blockhash_len_t max_blockhash2_len = IsShort |
|||
? digest_params::max_blockhash_len / 2 |
|||
: digest_params::max_blockhash_len; |
|||
|
|||
// Maximum characters required to pretty-print "natural" digests
|
|||
public: |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<blockhash_len_t, digest_params::max_blockhash_len>, |
|||
safe_int::uvalue<blockhash_len_t, 2> |
|||
>::is_valid, |
|||
"max_blockhash_len * 2 must be in range of blockhash_len_t."); |
|||
static_assert( |
|||
safe_int::safe_add< |
|||
safe_int::safe_add< |
|||
safe_int::uvalue<size_t, 3>, |
|||
safe_int::uvalue<size_t, digest_blocksize::max_natural_digits> |
|||
>, |
|||
safe_int::safe_mul< |
|||
safe_int::uvalue<size_t, digest_params::max_blockhash_len>, |
|||
safe_int::uvalue<size_t, 2> |
|||
> |
|||
>::is_valid, |
|||
"max_blockhash_len * 2 + max_natural_digits + 3 must be in range of size_t."); |
|||
static constexpr const size_t max_natural_chars = |
|||
max_blockhash1_len + max_blockhash2_len + // two block hashes
|
|||
digest_blocksize::max_natural_digits + // block size
|
|||
3; // two colons and '\0' as a terminator
|
|||
// width == chars exclusing '\0' (== chars - 1)
|
|||
static constexpr const size_t max_natural_width = max_natural_chars - 1; |
|||
static constexpr const size_t max_natural_width_digits = |
|||
numeric_digits::in_decimal<size_t>(max_natural_width); |
|||
static_assert(max_natural_width != 0, "sanity check for max_natural_width failed."); |
|||
|
|||
// Data structure
|
|||
private: |
|||
char digest[max_blockhash1_len + max_blockhash2_len]; |
|||
blockhash_len_t blkhash1_len; |
|||
blockhash_len_t blkhash2_len; |
|||
digest_blocksize_t blksize; |
|||
public: |
|||
size_t blockhash1_len(void) const noexcept { return blkhash1_len; } |
|||
size_t blockhash2_len(void) const noexcept { return blkhash2_len; } |
|||
unsigned long blocksize(void) const noexcept { return blksize; } |
|||
size_t digest_size_used(void) const noexcept { return blkhash1_len + blkhash2_len; } |
|||
const char* digest_buffer(void) const noexcept { return digest; } |
|||
void copy_digest_buffer(char* buf) const noexcept { memcpy(buf, digest, digest_size_used()); } |
|||
|
|||
// "Initialization" and assignment
|
|||
public: |
|||
digest_data(void) noexcept = default; // initialize to undefined state
|
|||
digest_data(const digest_data& other) noexcept |
|||
: blkhash1_len(other.blkhash1_len) |
|||
, blkhash2_len(other.blkhash2_len) |
|||
, blksize(other.blksize) |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
if (digest != other.digest) |
|||
memcpy(digest, other.digest, blkhash1_len + blkhash2_len); |
|||
} |
|||
const digest_data& operator=(const digest_data& other) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
blkhash1_len = other.blkhash1_len; |
|||
blkhash2_len = other.blkhash2_len; |
|||
blksize = other.blksize; |
|||
if (digest != other.digest) |
|||
memcpy(digest, other.digest, blkhash1_len + blkhash2_len); |
|||
return *this; |
|||
} |
|||
static void swap(digest_data& a, digest_data& b) noexcept |
|||
{ |
|||
std::swap(a.blksize, b.blksize); |
|||
std::swap(a.blkhash1_len, b.blkhash1_len); |
|||
std::swap(a.blkhash2_len, b.blkhash2_len); |
|||
std::swap(a.digest, b.digest); // C++11 version of swap
|
|||
} |
|||
|
|||
// Validators (for its validness and naturality)
|
|||
public: |
|||
bool is_valid(void) const noexcept |
|||
{ |
|||
if (blkhash1_len > max_blockhash1_len) |
|||
return false; |
|||
if (blkhash2_len > max_blockhash2_len) |
|||
return false; |
|||
if (blksize > 0xfffffffful) |
|||
return false; |
|||
if (IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (digest[i] < char(0) || 64 <= digest[i]) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
bool is_natural(void) const noexcept |
|||
{ |
|||
if (!is_valid()) |
|||
return false; |
|||
if (blkhash1_len < blkhash2_len) |
|||
return false; |
|||
if (!digest_blocksize::is_natural(blksize)) |
|||
return false; |
|||
if (!digest_blocksize::is_safe_to_double(blksize) && blkhash2_len >= 2) |
|||
return false; |
|||
if (!IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (!base64::isbase64(digest[i])) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
bool is_blocksize_natural(void) const noexcept |
|||
{ |
|||
return digest_blocksize::is_natural(blksize); |
|||
} |
|||
bool is_normalized(void) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
return |
|||
!strings::sequences<digest_params::max_blockhash_sequence> |
|||
::has_sequences(digest, blkhash1_len) && |
|||
!strings::sequences<digest_params::max_blockhash_sequence> |
|||
::has_sequences(digest+blkhash1_len, blkhash2_len); |
|||
} |
|||
bool has_valid_base64_data(void) const noexcept |
|||
{ |
|||
if (blkhash1_len > max_blockhash1_len) |
|||
return false; |
|||
if (blkhash2_len > max_blockhash2_len) |
|||
return false; |
|||
if (IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (digest[i] < char(0) || 64 <= digest[i]) |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
if (!base64::isbase64(digest[i])) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Equality
|
|||
private: |
|||
static bool is_eq_except_blocksize(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
return |
|||
a.blkhash1_len == b.blkhash1_len && |
|||
a.blkhash2_len == b.blkhash2_len && |
|||
memcmp(a.digest, b.digest, a.digest_size_used()) == 0; |
|||
} |
|||
static bool is_eq(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
return |
|||
digest_blocksize::is_near_eq(a.blksize, b.blksize) && |
|||
is_eq_except_blocksize(a, b); |
|||
} |
|||
public: |
|||
friend bool operator==(const digest_data& a, const digest_data& b) noexcept { return is_eq(a, b); } |
|||
friend bool operator!=(const digest_data& a, const digest_data& b) noexcept { return !is_eq(a, b); } |
|||
|
|||
// Default comparison for sorting (in "dictionary" order or whatever)
|
|||
// Note that sort order differs depending on IsAlphabetRestricted.
|
|||
public: |
|||
friend bool operator<(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blksize < b.blksize) |
|||
return true; |
|||
if (a.blksize > b.blksize) |
|||
return false; |
|||
if (a.blkhash1_len < b.blkhash1_len) |
|||
return true; |
|||
if (a.blkhash1_len > b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len < b.blkhash2_len) |
|||
return true; |
|||
if (a.blkhash2_len > b.blkhash2_len) |
|||
return false; |
|||
if (memcmp(a.digest, b.digest, a.digest_size_used()) < 0) |
|||
return true; |
|||
return false; |
|||
} |
|||
friend bool operator>(const digest_data& a, const digest_data& b) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blksize > b.blksize) |
|||
return true; |
|||
if (a.blksize < b.blksize) |
|||
return false; |
|||
if (a.blkhash1_len > b.blkhash1_len) |
|||
return true; |
|||
if (a.blkhash1_len < b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len > b.blkhash2_len) |
|||
return true; |
|||
if (a.blkhash2_len < b.blkhash2_len) |
|||
return false; |
|||
if (memcmp(a.digest, b.digest, a.digest_size_used()) > 0) |
|||
return true; |
|||
return false; |
|||
} |
|||
friend bool operator<=(const digest_data& a, const digest_data& b) noexcept { return !(a > b); } |
|||
friend bool operator>=(const digest_data& a, const digest_data& b) noexcept { return !(a < b); } |
|||
|
|||
// Predicates (including non-standard comparison predicates for fast sorting)
|
|||
public: |
|||
struct pred_equal_to |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a == b; } |
|||
}; |
|||
struct pred_not_equal_to |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a != b; } |
|||
}; |
|||
struct pred_less |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a < b; } |
|||
}; |
|||
struct pred_less_equal |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a <= b; } |
|||
}; |
|||
struct pred_greater |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a > b; } |
|||
}; |
|||
struct pred_greater_equal |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept { return a >= b; } |
|||
}; |
|||
struct pred_less_blocksize |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept |
|||
{ |
|||
return a.blksize < b.blksize; |
|||
} |
|||
}; |
|||
struct pred_less_blocksize_natural |
|||
{ |
|||
bool operator()(const digest_data& a, const digest_data& b) const noexcept |
|||
{ |
|||
bool aN = digest_blocksize::is_natural(a.blksize); |
|||
bool bN = digest_blocksize::is_natural(b.blksize); |
|||
if (aN && !bN) |
|||
return false; |
|||
if (!aN && bN) |
|||
return true; |
|||
if (a.blksize < b.blksize) |
|||
return true; |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
// Hash (for sets and dictionaries)
|
|||
public: |
|||
size_t hash(void) const noexcept |
|||
{ |
|||
typedef typename std::conditional< |
|||
(std::numeric_limits<size_t>::max() >= 0xfffffffful), |
|||
size_t, uint_least32_t |
|||
>::type hash_t; |
|||
static constexpr const hash_t fnv_init = 2166136261ul; |
|||
static constexpr const hash_t fnv_prime = 16777619ul; |
|||
hash_t h = fnv_init; |
|||
h ^= hash_t(blksize); h *= fnv_prime; |
|||
h ^= hash_t(blkhash1_len); h *= fnv_prime; |
|||
h ^= hash_t(blkhash2_len); h *= fnv_prime; |
|||
for (blockhash_len_t i = 0, l = blkhash1_len + blkhash2_len; i < l; i++) |
|||
{ |
|||
h ^= hash_t(static_cast<unsigned char>(digest[i])); |
|||
h *= fnv_prime; |
|||
} |
|||
if (std::numeric_limits<size_t>::max() < 0xfffffffful) |
|||
h ^= (h >> 16); |
|||
return size_t(h); |
|||
} |
|||
|
|||
// Normalization
|
|||
public: |
|||
static void normalize(digest_data& dest, const digest_data& source) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(source.is_valid()); |
|||
#endif |
|||
dest.blksize = source.blksize; |
|||
blockhash_len_t orig_blkhash1_len = source.blkhash1_len; |
|||
dest.blkhash1_len = strings::sequences<digest_params::max_blockhash_sequence> |
|||
::copy_elim_sequences(dest.digest, source.digest, source.blkhash1_len); |
|||
dest.blkhash2_len = strings::sequences<digest_params::max_blockhash_sequence> |
|||
::copy_elim_sequences(dest.digest + dest.blkhash1_len, source.digest + orig_blkhash1_len, source.blkhash2_len); |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(dest.is_valid()); |
|||
assert(dest.is_normalized()); |
|||
#endif |
|||
} |
|||
template <typename Treturn = digest_data> |
|||
static Treturn normalize(const digest_data& source) noexcept |
|||
{ |
|||
Treturn dest; |
|||
digest_data::normalize(dest, source); |
|||
return dest; |
|||
} |
|||
void normalize(void) noexcept |
|||
{ |
|||
normalize(*this, *this); |
|||
} |
|||
|
|||
// Utility to parse digests
|
|||
private: |
|||
template <template <char... terms> class Tstring_copy> |
|||
static bool parse_internal(digest_data& digest, const char* str) noexcept |
|||
{ |
|||
const char* rem = str; |
|||
char* out = digest.digest; |
|||
errno = 0; |
|||
// size of unsigned long is at least 32-bit
|
|||
unsigned long blksize = strtoul(str, const_cast<char**>(&rem), 10); |
|||
if (rem == str) |
|||
return false; |
|||
if (errno == ERANGE && blksize == std::numeric_limits<unsigned long>::max()) |
|||
return false; |
|||
if (blksize > 0xfffffffful) |
|||
return false; |
|||
digest.blksize = digest_blocksize_t(blksize); |
|||
if (*rem++ != ':') |
|||
return false; |
|||
if (!Tstring_copy<':'>::copy_elim_sequences(out, max_blockhash1_len, rem)) |
|||
return false; |
|||
digest.blkhash1_len = out - digest.digest; |
|||
char* outtmp = out; |
|||
if (*rem++ != ':') |
|||
return false; |
|||
#ifndef FFUZZYPP_BLOCKDIGEST2_TERMS |
|||
// default from ffuzzy++ 4.0
|
|||
#define FFUZZYPP_BLOCKDIGEST2_TERMS ',' |
|||
#endif |
|||
if (!Tstring_copy<FFUZZYPP_BLOCKDIGEST2_TERMS>:: |
|||
copy_elim_sequences(out, max_blockhash2_len, rem)) |
|||
return false; |
|||
digest.blkhash2_len = out - outtmp; |
|||
if (IsAlphabetRestricted) |
|||
{ |
|||
for (blockhash_len_t i = 0; i < digest.blkhash1_len + digest.blkhash2_len; i++) |
|||
{ |
|||
if (digest.digest[i] == base64::invalid_index) |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
protected: |
|||
static bool parse(digest_data& digest, const char* str) noexcept |
|||
{ |
|||
return parse_internal< |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::input_type>::template string_copy |
|||
>(digest, str); |
|||
} |
|||
static bool parse(digest_data& digest, const std::string& str) |
|||
{ |
|||
return parse(digest, str.c_str()); |
|||
} |
|||
static bool parse_normalized(digest_data& digest, const char* str) noexcept |
|||
{ |
|||
return parse_internal< |
|||
strings::sequences<digest_params::max_blockhash_sequence, |
|||
typename digest_data_transformation<IsAlphabetRestricted>::input_type>::template string_copy |
|||
>(digest, str); |
|||
} |
|||
static bool parse_normalized(digest_data& digest, const std::string& str) |
|||
{ |
|||
return parse_normalized(digest, str.c_str()); |
|||
} |
|||
|
|||
// Pretty printing
|
|||
public: |
|||
bool pretty_unsafe(char* out) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
int n = sprintf(out, "%lu:", static_cast<unsigned long>(blksize)); |
|||
if (n < 0) |
|||
return false; |
|||
out += n; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest, blkhash1_len); |
|||
out += blkhash1_len; |
|||
*out++ = ':'; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest+blkhash1_len, blkhash2_len); |
|||
out[blkhash2_len] = '\0'; |
|||
return true; |
|||
} |
|||
bool pretty(char* out, size_t outsize) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
int n = snprintf(out, outsize, "%lu:", static_cast<unsigned long>(blksize)); |
|||
if (n < 0) |
|||
return false; |
|||
if (size_t(n) == outsize) |
|||
return false; |
|||
outsize -= size_t(n); |
|||
out += n; |
|||
if (outsize < size_t(blkhash1_len) + size_t(blkhash2_len) + 2) |
|||
return false; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest, blkhash1_len); |
|||
out += blkhash1_len; |
|||
*out++ = ':'; |
|||
strings::nosequences<typename digest_data_transformation<IsAlphabetRestricted>::output_type>::copy_raw(out, digest+blkhash1_len, blkhash2_len); |
|||
out[blkhash2_len] = '\0'; |
|||
return true; |
|||
} |
|||
std::string pretty(void) const |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(is_valid()); |
|||
#endif |
|||
unsigned long bs(blksize); |
|||
std::string str(std::to_string(bs)); |
|||
str.push_back(':'); |
|||
for (blockhash_len_t i = 0; i < blkhash1_len; i++) |
|||
str.push_back(digest_data_transformation<IsAlphabetRestricted>::output_type::transform(digest[i])); |
|||
str.push_back(':'); |
|||
for (blockhash_len_t i = 0; i < blkhash2_len; i++) |
|||
str.push_back(digest_data_transformation<IsAlphabetRestricted>::output_type::transform(digest[blkhash1_len + i])); |
|||
return str; |
|||
} |
|||
|
|||
// Friend classes
|
|||
template <comparison_version> friend class internal::digest_comparison_base; |
|||
template <comparison_version> friend class digest_comparison; |
|||
template <bool> friend class digest_position_array_base; |
|||
friend class digest_generator; |
|||
friend class internal::digest_copy; |
|||
}; |
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
// Utility to copy constrained digest data to non-constrained digest object
|
|||
class digest_copy |
|||
{ |
|||
private: |
|||
digest_copy(void) = delete; |
|||
digest_copy(const digest_copy&) = delete; |
|||
public: |
|||
template <bool IsAlphabetRestricted> |
|||
static void copy_to_long( |
|||
digest_data<IsAlphabetRestricted, false>& dest, |
|||
const digest_data<IsAlphabetRestricted, true>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
memcpy(dest.digest, src.digest, src.blkhash1_len + src.blkhash2_len); |
|||
} |
|||
template <bool IsShort> |
|||
static void copy_to_non_ra( |
|||
digest_data<false, IsShort>& dest, |
|||
const digest_data<true, IsShort>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
strings::nosequences<base64::transform_to_b64>:: |
|||
copy_raw(dest.digest, src.digest, src.blkhash1_len + src.blkhash2_len); |
|||
} |
|||
static void copy_to_long_non_ra( |
|||
digest_data<false, false>& dest, |
|||
const digest_data<true, true>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
strings::nosequences<base64::transform_to_b64>:: |
|||
copy_raw(dest.digest, src.digest, src.blkhash1_len + src.blkhash2_len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,125 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_filesize.hpp |
|||
File size utilities |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_FILESIZE_HPP |
|||
#define FFUZZYPP_DIGEST_FILESIZE_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "utils/safe_int.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
typedef uint_least64_t digest_filesize_t; |
|||
|
|||
// File size utilities
|
|||
class digest_filesize |
|||
{ |
|||
static_assert(safe_int::contracts::is_unsigned_integral_type<digest_filesize_t>(), |
|||
"digest_filesize_t must be an unsigned integral type."); |
|||
private: |
|||
digest_filesize(void) = delete; |
|||
digest_filesize(const digest_filesize&) = delete; |
|||
|
|||
// Minimum size (as supported by ssdeep)
|
|||
private: |
|||
static constexpr const unsigned long u_min_supported_size = 4096 + 1; |
|||
public: |
|||
static_assert(u_min_supported_size <= 0xfffffffful, |
|||
"u_min_supported_size must be in range of 32-bit unsigned integer."); |
|||
static_assert(safe_int::uvalue<digest_filesize_t, u_min_supported_size>::is_valid, |
|||
"u_min_supported_size must be in range of digest_filesize_t."); |
|||
static constexpr const digest_filesize_t min_supported_size = u_min_supported_size; |
|||
|
|||
// Maximum size (theoretical limit)
|
|||
public: |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::safe_mul< |
|||
safe_int::uvalue<digest_filesize_t, digest_blocksize::max_blocksize>, |
|||
safe_int::uvalue<digest_filesize_t, digest_params::max_blockhash_len> |
|||
>, |
|||
safe_int::uvalue<digest_filesize_t, 1> |
|||
>::is_valid, |
|||
"max_blocksize * max_blockhash_len + 1 (== max_size + 1) must be " |
|||
"in range of digest_filesize_t."); |
|||
static constexpr const digest_filesize_t max_size = |
|||
digest_filesize_t(digest_blocksize::max_blocksize) * |
|||
digest_filesize_t(digest_params::max_blockhash_len); |
|||
static constexpr const digest_filesize_t max_theoretical_size = max_size; |
|||
|
|||
// Maximum portable size (with ssdeep 2.6-2.12)
|
|||
public: |
|||
static_assert(safe_int::uvalue<digest_filesize_t, 0xfffffffful>::is_valid, |
|||
"digest_filesize_t must be able to represent all 32-bit unsigned integer values."); |
|||
static constexpr const digest_filesize_t max_portable_size = |
|||
minmax::min(max_size, digest_filesize_t(0xfffffffful)); |
|||
static_assert(min_supported_size <= max_portable_size, |
|||
"min_supported_size must not exceed max_portable_size."); |
|||
|
|||
// Predicates
|
|||
public: |
|||
static constexpr bool is_portable(digest_filesize_t total_size) noexcept |
|||
{ |
|||
/*
|
|||
WARNING: Given size may not be "portable" on insane architectures. |
|||
|
|||
Portability: |
|||
* ffuzzy++ (3.0) |
|||
* ssdeep (including 2.6-2.12) |
|||
|
|||
Note: |
|||
Mr.Kornblum (the original author of ssdeep) considers |
|||
version 2.9 the "standard". Note that this release does not |
|||
support files equal to or larger than 4GiB. |
|||
*/ |
|||
return total_size >= min_supported_size && total_size <= max_portable_size; |
|||
} |
|||
static constexpr bool is_supported_by_ssdeep_2_12(digest_filesize_t total_size) noexcept |
|||
{ |
|||
return total_size >= min_supported_size && total_size <= max_portable_size; |
|||
} |
|||
static constexpr bool is_supported_by_ffuzzy_3_0(digest_filesize_t total_size) noexcept |
|||
{ |
|||
return total_size <= max_theoretical_size; |
|||
} |
|||
static constexpr bool is_supported(digest_filesize_t total_size) noexcept |
|||
{ |
|||
// whether supported by "this" version of ffuzzy++
|
|||
return is_supported_by_ffuzzy_3_0(total_size); |
|||
} |
|||
|
|||
// Predicate (equivalent to "not meaningful results" in ssdeep)
|
|||
public: |
|||
static constexpr bool is_not_meaningful(digest_filesize_t total_size) noexcept |
|||
{ |
|||
return total_size < min_supported_size; |
|||
} |
|||
}; |
|||
|
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,544 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_generator.hpp |
|||
Fuzzy digest generator |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_GENERATOR_HPP |
|||
#define FFUZZYPP_DIGEST_GENERATOR_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstdint> |
|||
#include <cstddef> |
|||
|
|||
#include <algorithm> |
|||
#include <string> |
|||
|
|||
#include "base64.hpp" |
|||
#include "context_hash.hpp" |
|||
#include "context_hash_fast.hpp" |
|||
#include "rolling_hash.hpp" |
|||
#include "rolling_hash_ssdeep.hpp" |
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "digest_base.hpp" |
|||
#include "digest_filesize.hpp" |
|||
#include "utils/likely.hpp" |
|||
#include "utils/safe_int.hpp" |
|||
#include "strings/transform.hpp" |
|||
#include "strings/sequences.hpp" |
|||
#include "strings/nosequences.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
struct digest_generator_error {}; |
|||
|
|||
class digest_generator |
|||
{ |
|||
// Digest characters and transformation for them
|
|||
private: |
|||
/*
|
|||
This type contains digest characters as Base64 indices. |
|||
To distinguish valid and invalid characters, |
|||
we need a "nil" value. |
|||
|
|||
We transform Base64 indices to actual Base64 characters |
|||
when we are copying final digest value. |
|||
*/ |
|||
typedef base64::transform_to_b64 digest_transform_t; |
|||
static constexpr const char digest_nil = 64; |
|||
static_assert(digest_nil < 0 || digest_nil > 63, |
|||
"digest_nil must be out of Base64 range."); |
|||
|
|||
// Heuristics to guess maximum file size from block size (or its index)
|
|||
public: |
|||
static constexpr digest_filesize_t guessed_filesize(digest_blocksize_t blocksize) noexcept |
|||
{ |
|||
return digest_filesize_t(blocksize) * digest_params::max_blockhash_len; |
|||
} |
|||
static constexpr digest_filesize_t guessed_filesize_at(unsigned i) noexcept |
|||
{ |
|||
return guessed_filesize(digest_blocksize::at(i)); |
|||
} |
|||
|
|||
// Heuristic to guess block size from file size
|
|||
public: |
|||
static unsigned blockhash_index_guessed_by_filesize(digest_filesize_t size, unsigned start = 0) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(start < digest_blocksize::number_of_blockhashes); |
|||
assert(size <= digest_filesize::max_size); |
|||
#endif |
|||
unsigned bi = start; |
|||
digest_filesize_t bgs = guessed_filesize_at(bi); |
|||
while (bgs < size) |
|||
{ |
|||
bi++; |
|||
bgs *= 2; |
|||
} |
|||
return bi; |
|||
} |
|||
|
|||
// Data Structure
|
|||
private: |
|||
typedef context_hash_fast context_hash_t; |
|||
struct blockhash_context |
|||
{ |
|||
context_hash_t hfull; |
|||
context_hash_t hhalf; |
|||
char digest[digest_params::max_blockhash_len]; |
|||
char digesth; |
|||
blockhash_len_t dindex; |
|||
}; |
|||
blockhash_context bh[digest_blocksize::number_of_blockhashes]; |
|||
digest_filesize_t totalsz; |
|||
digest_filesize_t totalsz_constant; |
|||
digest_filesize_t reduce_border; |
|||
rolling_hash roll; |
|||
unsigned bhstart; |
|||
unsigned bhend; |
|||
unsigned bhendlimit; |
|||
uint_least32_t rollmask; |
|||
context_hash_t hlast; |
|||
unsigned flags; |
|||
static constexpr const unsigned FLAG_LASTHASH = 0x1; |
|||
static constexpr const unsigned FLAG_SZCONSTANT = 0x2; |
|||
|
|||
// Simple data structure manipulation
|
|||
public: |
|||
bool is_total_size_clamped(void) const noexcept { return totalsz > digest_filesize::max_size; } |
|||
bool is_file_size_constant(void) const noexcept { return flags & FLAG_SZCONSTANT; } |
|||
unsigned blockhash_index_start(void) const noexcept { return bhstart; } |
|||
unsigned blockhash_index_end(void) const noexcept { return bhend; } |
|||
unsigned blockhash_index_end_limit(void) const noexcept { return bhendlimit; } |
|||
digest_filesize_t total_size(void) const noexcept { return totalsz; } |
|||
// constant file size (for fast ssdeep hashing)
|
|||
digest_filesize_t get_file_size_constant(void) const noexcept { return totalsz_constant; } |
|||
bool set_file_size_constant(digest_filesize_t size) noexcept |
|||
{ |
|||
if (is_file_size_constant() && totalsz_constant != size) |
|||
return false; |
|||
if (size > digest_filesize::max_size) |
|||
return false; |
|||
totalsz_constant = size; |
|||
bhendlimit = std::min( |
|||
digest_blocksize::number_of_blockhashes - 1, |
|||
blockhash_index_guessed_by_filesize(size) + 1 |
|||
); |
|||
flags |= FLAG_SZCONSTANT; |
|||
return true; |
|||
} |
|||
|
|||
public: |
|||
// Reset minimum context required
|
|||
void reset(void) noexcept |
|||
{ |
|||
bh[0].hfull.reset(); |
|||
bh[0].hhalf.reset(); |
|||
bh[0].digest[digest_params::max_blockhash_len - 1] = digest_nil; |
|||
bh[0].digesth = digest_nil; |
|||
bh[0].dindex = 0; |
|||
totalsz = 0; |
|||
reduce_border = guessed_filesize_at(0); |
|||
roll.reset(); |
|||
bhstart = 0; |
|||
bhend = 1; |
|||
bhendlimit = digest_blocksize::number_of_blockhashes - 1; |
|||
rollmask = 0; |
|||
flags = 0; |
|||
} |
|||
|
|||
// Update functions (by buffer or by character)
|
|||
public: |
|||
void update(const unsigned char* buf, size_t len) noexcept |
|||
{ |
|||
rolling_hash r = roll; |
|||
if (FFUZZYPP_UNLIKELY(len > digest_filesize::max_size |
|||
|| digest_filesize::max_size - digest_filesize_t(len) < totalsz)) |
|||
{ |
|||
totalsz = digest_filesize::max_size + 1; |
|||
} |
|||
else |
|||
{ |
|||
totalsz += digest_filesize_t(len); |
|||
} |
|||
while (len--) |
|||
{ |
|||
unsigned char c = *buf++; |
|||
r.update(c); |
|||
for (unsigned i = bhstart; i < bhend; i++) |
|||
{ |
|||
bh[i].hfull.update(c); |
|||
bh[i].hhalf.update(c); |
|||
} |
|||
if (flags & FLAG_LASTHASH) |
|||
hlast.update(c); |
|||
uint_least32_t horg = (r.sum() + 1) & uint_least32_t(0xfffffffful); |
|||
uint_least32_t h = horg / uint_least32_t(digest_blocksize::min_blocksize); |
|||
if (0xfffffffful % digest_blocksize::min_blocksize != digest_blocksize::min_blocksize - 1 && !horg) |
|||
continue; |
|||
if (FFUZZYPP_LIKELY(h & rollmask)) |
|||
continue; |
|||
if (horg % uint_least32_t(digest_blocksize::min_blocksize)) |
|||
continue; |
|||
h >>= bhstart; |
|||
unsigned i = bhstart; |
|||
do |
|||
{ |
|||
if (FFUZZYPP_UNLIKELY(bh[i].dindex == 0)) |
|||
{ |
|||
// fork to prepare larger block sizes
|
|||
if (bhend > bhendlimit) |
|||
{ |
|||
if (bhendlimit == digest_blocksize::number_of_blockhashes - 1 |
|||
&& !(flags & FLAG_LASTHASH)) |
|||
{ |
|||
hlast = bh[i].hfull; |
|||
flags |= FLAG_LASTHASH; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
bh[i+1].hfull = bh[i].hfull; |
|||
bh[i+1].hhalf = bh[i].hhalf; |
|||
bh[i+1].digest[digest_params::max_blockhash_len - 1] = digest_nil; |
|||
bh[i+1].digesth = digest_nil; |
|||
bh[i+1].dindex = 0; |
|||
bhend++; |
|||
} |
|||
} |
|||
bh[i].digest[bh[i].dindex] = bh[i].hfull.sum_in_base64(); |
|||
bh[i].digesth = bh[i].hhalf.sum_in_base64(); |
|||
if (bh[i].dindex < digest_params::max_blockhash_len - 1) |
|||
{ |
|||
bh[i].dindex++; |
|||
bh[i].hfull.reset(); |
|||
if (bh[i].dindex < digest_params::max_blockhash_len / 2) |
|||
{ |
|||
bh[i].digesth = digest_nil; |
|||
bh[i].hhalf.reset(); |
|||
} |
|||
} |
|||
// eliminate block sizes which will not be chosen
|
|||
else if (FFUZZYPP_UNLIKELY(bhend - bhstart >= 2 |
|||
&& reduce_border < (is_file_size_constant() ? totalsz_constant : totalsz) |
|||
&& bh[i+1].dindex >= digest_params::max_blockhash_len / 2)) |
|||
{ |
|||
bhstart++; |
|||
rollmask = rollmask * 2 + 1; |
|||
reduce_border *= 2; |
|||
} |
|||
if (h & 1) |
|||
break; |
|||
h >>= 1; |
|||
} while (++i < bhend); |
|||
} |
|||
roll = r; |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
unsigned char C(c); |
|||
update(&C, 1); |
|||
} |
|||
|
|||
// High-level update utilities
|
|||
// (by file pointer or by file name; w/ or w/o internal buffer)
|
|||
public: |
|||
static constexpr const size_t default_buffer_size = 4096; |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_stream(FILE* fp, unsigned char* tmpbuf) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
if (!fp) |
|||
return false; |
|||
while (true) |
|||
{ |
|||
size_t n = fread(tmpbuf, 1, buffer_size, fp); |
|||
if (n == 0) |
|||
break; |
|||
update(tmpbuf, n); |
|||
} |
|||
if (feof(fp)) |
|||
return true; |
|||
return false; |
|||
} |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_stream(FILE* fp) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
unsigned char buf[buffer_size]; |
|||
return update_by_stream<buffer_size>(fp, buf); |
|||
} |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_file(const char* filename) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
FILE* fp = fopen(filename, "rb"); |
|||
if (!fp) |
|||
return false; |
|||
bool ret = update_by_stream<buffer_size>(fp); |
|||
fclose(fp); |
|||
return ret; |
|||
} |
|||
template <size_t buffer_size = default_buffer_size> |
|||
bool update_by_file(const char* filename, unsigned char* tmpbuf) noexcept |
|||
{ |
|||
static_assert(buffer_size != 0, "buffer_size must not be zero."); |
|||
FILE* fp = fopen(filename, "rb"); |
|||
if (!fp) |
|||
return false; |
|||
bool ret = update_by_stream<buffer_size>(fp, tmpbuf); |
|||
fclose(fp); |
|||
return ret; |
|||
} |
|||
|
|||
// Digest finalization
|
|||
private: |
|||
// Heuristic to guess block hash index to start from the current state
|
|||
unsigned blockhash_index_guessed_to_start(void) const noexcept |
|||
{ |
|||
unsigned bi = blockhash_index_guessed_by_filesize(totalsz, bhstart); |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(bi < digest_blocksize::number_of_blockhashes); |
|||
#endif |
|||
bi = std::min(bi, bhend - 1); |
|||
while (bi > bhstart && bh[bi].dindex < digest_params::max_blockhash_len / 2) |
|||
bi--; |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(bi >= bhstart && bi < bhend); |
|||
assert(bi == 0 || bh[bi].dindex >= digest_params::max_blockhash_len / 2); |
|||
#endif |
|||
return bi; |
|||
} |
|||
// Copy the final (resulting) digest
|
|||
template <typename Tseq, bool IsAlphabetRestricted, bool IsShort, bool Shortened> |
|||
bool copy_digest_internal(digest_data<IsAlphabetRestricted, IsShort>& digest) noexcept |
|||
{ |
|||
/*
|
|||
This function is not exactly "const" but mostly constant. |
|||
You can call this function multiple times as you need. |
|||
*/ |
|||
static_assert(Shortened == true || IsShort == false, |
|||
"copying long result to short digest_data structure is prohibited."); |
|||
if (is_total_size_clamped()) |
|||
return false; |
|||
if (is_file_size_constant() && totalsz != totalsz_constant) |
|||
return false; |
|||
unsigned bi = blockhash_index_guessed_to_start(); |
|||
digest.blksize = digest_blocksize::at(bi); |
|||
|
|||
uint_least32_t rh = roll.sum(); |
|||
// Copy first block hash (digest)
|
|||
{ |
|||
char chtmp = bh[bi].digest[digest_params::max_blockhash_len - 1]; |
|||
size_t sz = bh[bi].dindex; |
|||
if (rh != 0) |
|||
bh[bi].digest[sz++] = bh[bi].hfull.sum_in_base64(); |
|||
else if (chtmp != digest_nil) |
|||
sz++; |
|||
digest.blkhash1_len = Tseq::copy_elim_sequences(digest.digest, bh[bi].digest, sz); |
|||
bh[bi].digest[digest_params::max_blockhash_len - 1] = chtmp; |
|||
} |
|||
// Copy second block hash if we need
|
|||
if (bi < bhend - 1) |
|||
{ |
|||
size_t dindex = bh[bi+1].dindex; |
|||
if (Shortened) |
|||
dindex = std::min(dindex, size_t(digest_params::max_blockhash_len / 2 - 1)); |
|||
char chtmp = bh[bi+1].digest[dindex]; |
|||
size_t sz = dindex; |
|||
if (rh != 0) |
|||
{ |
|||
bh[bi+1].digest[sz++] = Shortened |
|||
? bh[bi+1].hhalf.sum_in_base64() |
|||
: bh[bi+1].hfull.sum_in_base64(); |
|||
} |
|||
else |
|||
{ |
|||
if (Shortened) |
|||
{ |
|||
if (bh[bi+1].digesth != digest_nil) |
|||
bh[bi+1].digest[sz++] = bh[bi+1].digesth; |
|||
} |
|||
else |
|||
{ |
|||
if (dindex == digest_params::max_blockhash_len - 1 && chtmp != digest_nil) |
|||
sz++; |
|||
} |
|||
} |
|||
digest.blkhash2_len = Tseq::copy_elim_sequences( |
|||
digest.digest + digest.blkhash1_len, bh[bi+1].digest, sz); |
|||
bh[bi+1].digest[dindex] = chtmp; |
|||
} |
|||
else if (rh != 0) |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(bi == 0 || bi == digest_blocksize::number_of_blockhashes - 1); |
|||
#endif |
|||
if (bi == 0) |
|||
digest.digest[digest.blkhash1_len] = bh[bi].hfull.sum_in_base64(); |
|||
else |
|||
digest.digest[digest.blkhash1_len] = hlast.sum_in_base64(); |
|||
digest.blkhash2_len = Tseq::copy_elim_sequences( |
|||
digest.digest + digest.blkhash1_len, |
|||
digest.digest + digest.blkhash1_len, |
|||
1); |
|||
} |
|||
else |
|||
{ |
|||
digest.blkhash2_len = 0; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Digest finalization (and copying) utilities
|
|||
private: |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool Shortened> |
|||
bool copy_digest_base(digest_data<IsAlphabetRestricted, IsShort>& digest) noexcept |
|||
{ |
|||
return copy_digest_internal< |
|||
strings::nosequences< |
|||
typename digest_data_transformation<!IsAlphabetRestricted>::output_type |
|||
>, |
|||
IsAlphabetRestricted, IsShort, Shortened>(digest); |
|||
} |
|||
public: |
|||
template <bool IsAlphabetRestricted, bool IsShort, bool Shortened = true> |
|||
bool copy_digest_normalized(digest_data<IsAlphabetRestricted, IsShort>& digest) noexcept |
|||
{ |
|||
return copy_digest_internal< |
|||
strings::sequences< |
|||
digest_params::max_blockhash_sequence, |
|||
typename digest_data_transformation<!IsAlphabetRestricted>::output_type |
|||
>, IsAlphabetRestricted, IsShort, Shortened |
|||
>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
bool copy_digest(digest_base<IsAlphabetRestricted, IsShort, true>& digest) noexcept |
|||
{ |
|||
return copy_digest_normalized<IsAlphabetRestricted, IsShort, true>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted, bool IsShort> |
|||
bool copy_digest(digest_base<IsAlphabetRestricted, IsShort, false>& digest) noexcept |
|||
{ |
|||
return copy_digest_base<IsAlphabetRestricted, IsShort, true>(digest); |
|||
} |
|||
public: |
|||
template <bool IsAlphabetRestricted> |
|||
bool copy_digest_long_normalized(digest_base<IsAlphabetRestricted, false, true>& digest) noexcept |
|||
{ |
|||
return copy_digest_normalized<IsAlphabetRestricted, false, false>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted> |
|||
bool copy_digest_long(digest_base<IsAlphabetRestricted, false, true>& digest) noexcept |
|||
{ |
|||
return copy_digest_normalized<IsAlphabetRestricted, false, false>(digest); |
|||
} |
|||
template <bool IsAlphabetRestricted> |
|||
bool copy_digest_long(digest_base<IsAlphabetRestricted, false, false>& digest) noexcept |
|||
{ |
|||
return copy_digest_base<IsAlphabetRestricted, false, false>(digest); |
|||
} |
|||
private: |
|||
template <typename T, bool IsAlphabetRestricted, bool IsShort, bool IsNormalized, bool Shortened = true> |
|||
T digest_in_type(void) |
|||
{ |
|||
// This object to be erased by NRVO.
|
|||
T d; |
|||
if (IsNormalized) |
|||
{ |
|||
if (!copy_digest_normalized<IsAlphabetRestricted, IsShort, Shortened>(d)) |
|||
throw digest_generator_error(); |
|||
} |
|||
else |
|||
{ |
|||
if (!copy_digest_base<IsAlphabetRestricted, IsShort, Shortened>(d)) |
|||
throw digest_generator_error(); |
|||
} |
|||
return d; |
|||
} |
|||
public: |
|||
digest_unorm_t digest(void) |
|||
{ |
|||
// Default options as you need (unnormalized; short form)
|
|||
return digest_in_type<digest_unorm_t, false, true, false>(); |
|||
} |
|||
std::string digest_str(void) |
|||
{ |
|||
return digest().pretty(); |
|||
} |
|||
public: |
|||
template <bool Shortened = false> |
|||
digest_long_unorm_t digest_long(void) |
|||
{ |
|||
return digest_in_type<digest_long_unorm_t, false, false, false, Shortened>(); |
|||
} |
|||
digest_t digest_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_t, false, true, true>(); |
|||
} |
|||
template <bool Shortened = false> |
|||
digest_long_t digest_long_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_long_t, false, false, true, Shortened>(); |
|||
} |
|||
public: |
|||
digest_ra_unorm_t digest_ra(void) |
|||
{ |
|||
return digest_in_type<digest_ra_unorm_t, true, true, false>(); |
|||
} |
|||
template <bool Shortened = false> |
|||
digest_ra_long_unorm_t digest_ra_long(void) |
|||
{ |
|||
return digest_in_type<digest_ra_long_unorm_t, true, false, false, Shortened>(); |
|||
} |
|||
digest_ra_t digest_ra_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_ra_t, true, true, true>(); |
|||
} |
|||
template <bool Shortened = false> |
|||
digest_ra_long_t digest_ra_long_normalized(void) |
|||
{ |
|||
return digest_in_type<digest_ra_long_t, true, false, true, Shortened>(); |
|||
} |
|||
|
|||
// Constructors
|
|||
public: |
|||
digest_generator(void) noexcept |
|||
{ |
|||
reset(); |
|||
} |
|||
digest_generator(const digest_generator&) noexcept = default; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,425 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_position_array.hpp |
|||
Fuzzy digest (position array representation) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_POSITION_ARRAY_HPP |
|||
#define FFUZZYPP_DIGEST_POSITION_ARRAY_HPP |
|||
|
|||
#include <algorithm> |
|||
#include <string> |
|||
|
|||
#include "digest_base.hpp" |
|||
#include "digest_position_array_base.hpp" |
|||
#include "digest_comparison.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
template <bool IsAlphabetRestricted> |
|||
class digest_position_array |
|||
: public digest_position_array_base<IsAlphabetRestricted> |
|||
{ |
|||
public: |
|||
static constexpr const bool is_alphabet_restricted = IsAlphabetRestricted; |
|||
|
|||
public: |
|||
digest_position_array(void) noexcept = default; // initialize to undefined state
|
|||
digest_position_array(const digest_position_array& other) noexcept |
|||
: digest_position_array_base<IsAlphabetRestricted>(other) {} |
|||
const digest_position_array& operator=(const digest_position_array& other) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::operator=(other); |
|||
return *this; |
|||
} |
|||
|
|||
// Initialization by digest
|
|||
public: |
|||
template <bool IsShort> |
|||
digest_position_array(const digest_base<IsAlphabetRestricted, IsShort, true>& src) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(*this, src); |
|||
} |
|||
template <bool IsShort> |
|||
digest_position_array(const digest_base<IsAlphabetRestricted, IsShort, false>& src) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(*this, digest_data<IsAlphabetRestricted, IsShort>::normalize(src)); |
|||
} |
|||
public: |
|||
template <bool IsShort> |
|||
static void construct( |
|||
digest_position_array& dest, |
|||
const digest_base<IsAlphabetRestricted, IsShort, true>& src |
|||
) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(dest, src); |
|||
} |
|||
template <bool IsShort> |
|||
static void construct( |
|||
digest_position_array& dest, |
|||
const digest_base<IsAlphabetRestricted, IsShort, false>& src |
|||
) noexcept |
|||
{ |
|||
digest_position_array_base<IsAlphabetRestricted>::construct(dest, digest_data<IsAlphabetRestricted, IsShort>::normalize(src)); |
|||
} |
|||
|
|||
// Initialization by digest string
|
|||
public: |
|||
explicit digest_position_array(const char* str) noexcept(false) |
|||
{ |
|||
digest_base<IsAlphabetRestricted, false, true> digest(str); |
|||
construct(*this, digest); |
|||
} |
|||
explicit digest_position_array(const std::string& str) |
|||
: digest_position_array(str.c_str()) {} |
|||
|
|||
// Comparison
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_lt( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_lt(a, b); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_lt(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_lt(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_gt(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(other, *this); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_gt(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_lt<Version>(other, *this); |
|||
} |
|||
|
|||
// Comparison (on different digests)
|
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_diff( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_diff( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, false, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_position_array& a, |
|||
const digest_base<IsAlphabetRestricted, true, true>& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(a, b); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_base<IsAlphabetRestricted, false, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(b, a); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
static digest_comparison_score_t compare_near_eq_diff( |
|||
const digest_base<IsAlphabetRestricted, true, true>& a, |
|||
const digest_position_array& b |
|||
) noexcept |
|||
{ |
|||
return digest_comparison<Version>::compare_near_eq_diff(b, a); |
|||
} |
|||
public: |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_diff(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_diff(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_diff(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_diff(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq_diff(const digest_base<IsAlphabetRestricted, false, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq_diff<Version>(*this, other); |
|||
} |
|||
template <comparison_version Version = comparison_version::latest> |
|||
digest_comparison_score_t compare_near_eq_diff(const digest_base<IsAlphabetRestricted, true, true>& other) const noexcept |
|||
{ |
|||
return compare_near_eq_diff<Version>(*this, other); |
|||
} |
|||
}; |
|||
|
|||
typedef digest_position_array< true> digest_position_array_t; |
|||
typedef digest_position_array<false> digest_position_array_non_ra_t; |
|||
|
|||
} |
|||
|
|||
|
|||
// Specialization of standard swap
|
|||
namespace std |
|||
{ |
|||
template <bool IsAlphabetRestricted> |
|||
inline void swap( |
|||
ffuzzy::digest_position_array<IsAlphabetRestricted>& a, |
|||
ffuzzy::digest_position_array<IsAlphabetRestricted>& b |
|||
) noexcept |
|||
{ |
|||
ffuzzy::digest_position_array<IsAlphabetRestricted>::swap(a, b); |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,379 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
digest_position_array_base.hpp |
|||
Fuzzy digest (position array representation; base class) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_DIGEST_POSITION_ARRAY_BASE_HPP |
|||
#define FFUZZYPP_DIGEST_POSITION_ARRAY_BASE_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
|
|||
#include <algorithm> |
|||
#include <limits> |
|||
|
|||
#include "base64.hpp" |
|||
#include "digest_blocksize.hpp" |
|||
#include "digest_data.hpp" |
|||
#include "strings/position_array.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
template <bool IsAlphabetRestricted> |
|||
class digest_position_array_params |
|||
{ |
|||
private: |
|||
digest_position_array_params(void) = delete; |
|||
digest_position_array_params(const digest_position_array_params&) = delete; |
|||
public: |
|||
static constexpr const char char_min = |
|||
IsAlphabetRestricted ? 0x00 : std::numeric_limits<char>::min(); |
|||
static constexpr const char char_max = |
|||
IsAlphabetRestricted ? 0x3f : std::numeric_limits<char>::max(); |
|||
static constexpr const bool is_available = |
|||
strings::is_auto_position_array_available<digest_params::max_blockhash_len, char, char_min, char_max>::value; |
|||
}; |
|||
|
|||
|
|||
// Friend classes for digest_data class
|
|||
enum struct comparison_version; |
|||
namespace internal |
|||
{ |
|||
template <comparison_version> class digest_comparison_base; |
|||
} |
|||
template <comparison_version> class digest_comparison; |
|||
|
|||
|
|||
template <bool IsAlphabetRestricted> |
|||
class digest_position_array_base |
|||
{ |
|||
public: |
|||
typedef strings::auto_position_array< |
|||
digest_params::max_blockhash_len, char, |
|||
digest_position_array_params<IsAlphabetRestricted>::char_min, |
|||
digest_position_array_params<IsAlphabetRestricted>::char_max |
|||
> traits_type; |
|||
typedef typename traits_type::type pa_type; |
|||
typedef typename traits_type::int_type int_type; |
|||
|
|||
// Data structure
|
|||
private: |
|||
pa_type blkhash1; |
|||
pa_type blkhash2; |
|||
blockhash_len_t blkhash1_len; |
|||
blockhash_len_t blkhash2_len; |
|||
digest_blocksize_t blksize; |
|||
public: |
|||
size_t blockhash1_len(void) const noexcept { return blkhash1_len; } |
|||
size_t blockhash2_len(void) const noexcept { return blkhash2_len; } |
|||
unsigned long blocksize(void) const noexcept { return blksize; } |
|||
const pa_type& blockhash1_array(void) const noexcept { return blkhash1; } |
|||
const pa_type& blockhash2_array(void) const noexcept { return blkhash2; } |
|||
bool is_short(void) const noexcept { return blkhash2_len <= digest_params::max_blockhash_len / 2; } |
|||
|
|||
// "Initialization" and assignment
|
|||
public: |
|||
digest_position_array_base(void) noexcept = default; // initialize to undefined state
|
|||
digest_position_array_base(const digest_position_array_base& other) noexcept |
|||
: blkhash1(other.blkhash1) |
|||
, blkhash2(other.blkhash2) |
|||
, blkhash1_len(other.blkhash1_len) |
|||
, blkhash2_len(other.blkhash2_len) |
|||
, blksize(other.blksize) |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
} |
|||
const digest_position_array_base& operator=(const digest_position_array_base& other) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(other.is_valid()); |
|||
#endif |
|||
blkhash1 = other.blkhash1; |
|||
blkhash2 = other.blkhash2; |
|||
blkhash1_len = other.blkhash1_len; |
|||
blkhash2_len = other.blkhash2_len; |
|||
blksize = other.blksize; |
|||
return *this; |
|||
} |
|||
static void swap(digest_position_array_base& a, digest_position_array_base& b) noexcept |
|||
{ |
|||
std::swap(a.blkhash1, b.blkhash1); |
|||
std::swap(a.blkhash2, b.blkhash2); |
|||
std::swap(a.blkhash1_len, b.blkhash1_len); |
|||
std::swap(a.blkhash2_len, b.blkhash2_len); |
|||
std::swap(a.blksize, b.blksize); |
|||
} |
|||
|
|||
// Construction by digest
|
|||
protected: |
|||
template <bool IsShort> |
|||
static void construct( |
|||
digest_position_array_base& dest, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& src |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(src.is_valid()); |
|||
#endif |
|||
dest.blkhash1.construct(src.digest, src.blkhash1_len); |
|||
dest.blkhash2.construct(src.digest+src.blkhash1_len, src.blkhash2_len); |
|||
dest.blkhash1_len = src.blkhash1_len; |
|||
dest.blkhash2_len = src.blkhash2_len; |
|||
dest.blksize = src.blksize; |
|||
} |
|||
template <bool IsShort> |
|||
digest_position_array_base(const digest_data<IsAlphabetRestricted, IsShort>& src) noexcept |
|||
{ |
|||
construct(src); |
|||
} |
|||
|
|||
// Validators (for its validness and naturality)
|
|||
private: |
|||
bool is_valid_blockhash_position_array(const pa_type& parray, blockhash_len_t len) const noexcept |
|||
{ |
|||
int_type mask, set_bits; |
|||
const int_type* data = parray.bitmap_data(); |
|||
size_t array_size = parray.array_size; |
|||
if (len) |
|||
{ |
|||
mask = int_type(1u) << (len - 1); |
|||
mask = mask ^ (-mask); |
|||
} |
|||
else |
|||
{ |
|||
mask = -1; |
|||
} |
|||
set_bits = 0; |
|||
for (size_t i = 0; i < array_size; i++) |
|||
{ |
|||
int_type D = data[i]; |
|||
// check for invalid bits "above" length of the string
|
|||
if (D & mask) |
|||
return false; |
|||
// check for duplicate bits (e.g. n-th character of the string is BOTH 'A' and 'B')
|
|||
if (D & set_bits) |
|||
return false; |
|||
set_bits |= D; |
|||
// check for sequences
|
|||
int_type tmp = D; |
|||
for (blockhash_len_t j = 0; j < digest_params::max_blockhash_sequence; j++) |
|||
{ |
|||
tmp <<= 1; |
|||
tmp &= D; |
|||
} |
|||
if (tmp) |
|||
return false; |
|||
} |
|||
// check if set_bits is now "all valid bits"
|
|||
if (len) |
|||
{ |
|||
mask = int_type(1u) << (len - 1); |
|||
mask = mask | (mask - 1); |
|||
} |
|||
else |
|||
{ |
|||
mask = 0; |
|||
} |
|||
if (mask != set_bits) |
|||
return false; |
|||
return true; |
|||
} |
|||
bool is_natural_blockhash_position_array(const pa_type& parray, blockhash_len_t len) const noexcept |
|||
{ |
|||
if (!IsAlphabetRestricted) |
|||
{ |
|||
// check only if IsAlphabetRestricted is false
|
|||
// (represented by regular characters and non Base64 characters are possible)
|
|||
int_type mask, set_bits; |
|||
if (len) |
|||
{ |
|||
mask = int_type(1u) << (len - 1); |
|||
mask = mask | (mask - 1); |
|||
} |
|||
else |
|||
{ |
|||
set_bits = 0; |
|||
} |
|||
for (size_t i = 0; i < 64; i++) |
|||
set_bits |= parray[base64::values[i]]; |
|||
// check if all valid bits are set by valid Base64 chareacters
|
|||
if (mask != set_bits) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
public: |
|||
bool is_valid(void) const noexcept |
|||
{ |
|||
if (blkhash1_len > digest_params::max_blockhash_len) |
|||
return false; |
|||
if (blkhash2_len > digest_params::max_blockhash_len) |
|||
return false; |
|||
if (blksize > 0xfffffffful) |
|||
return false; |
|||
// Note: digest_position_array_base::is_valid takes time.
|
|||
if (!is_valid_blockhash_position_array(blkhash1, blkhash1_len)) |
|||
return false; |
|||
if (!is_valid_blockhash_position_array(blkhash2, blkhash2_len)) |
|||
return false; |
|||
return true; |
|||
} |
|||
bool is_natural(void) const noexcept |
|||
{ |
|||
if (!is_valid()) |
|||
return false; |
|||
if (blkhash1_len < blkhash2_len) |
|||
return false; |
|||
if (!digest_blocksize::is_natural(blksize)) |
|||
return false; |
|||
if (!digest_blocksize::is_safe_to_double(blksize) && blkhash2_len >= 2) |
|||
return false; |
|||
// Note: digest_position_array_base::is_natural takes time.
|
|||
if (!is_natural_blockhash_position_array(blkhash1, blkhash1_len)) |
|||
return false; |
|||
if (!is_natural_blockhash_position_array(blkhash2, blkhash2_len)) |
|||
return false; |
|||
return true; |
|||
} |
|||
bool is_blocksize_natural(void) const noexcept |
|||
{ |
|||
return digest_blocksize::is_natural(blksize); |
|||
} |
|||
|
|||
// Comparison
|
|||
public: |
|||
static bool is_eq_except_blocksize( |
|||
const digest_position_array_base& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blkhash1_len != b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len != b.blkhash2_len) |
|||
return false; |
|||
int_type data_a = a.bitmap_data(); |
|||
int_type data_b = b.bitmap_data(); |
|||
for (size_t i = 0; i < pa_type::array_size; i++) |
|||
{ |
|||
if (data_a[i] != data_b[i]) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
static bool is_eq( |
|||
const digest_position_array_base& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
if (a.blksize != b.blksize) |
|||
return false; |
|||
return is_eq_except_blocksize(a, b); |
|||
} |
|||
template <bool IsShort> |
|||
static bool is_eq_except_blocksize( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(a.is_valid()); |
|||
assert(b.is_valid()); |
|||
#endif |
|||
if (a.blkhash1_len != b.blkhash1_len) |
|||
return false; |
|||
if (a.blkhash2_len != b.blkhash2_len) |
|||
return false; |
|||
if (a.blksize != b.blksize) |
|||
return false; |
|||
const char* p = b.digest; |
|||
for (blockhash_len_t i = 0; i < b.blkhash1_len; i++) |
|||
{ |
|||
if (!(a.blkhash1[*p++] & (int_type(1u) << i))) |
|||
return false; |
|||
} |
|||
for (blockhash_len_t i = 0; i < b.blkhash2_len; i++) |
|||
{ |
|||
if (!(a.blkhash2[*p++] & (int_type(1u) << i))) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
template <bool IsShort> |
|||
static bool is_eq( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
if (a.blksize != b.blksize) |
|||
return false; |
|||
return is_eq_except_blocksize(a, b); |
|||
} |
|||
public: |
|||
friend bool operator==(const digest_position_array_base& a, const digest_position_array_base& b) noexcept { return is_eq(a, b); } |
|||
friend bool operator!=(const digest_position_array_base& a, const digest_position_array_base& b) noexcept { return !is_eq(a, b); } |
|||
public: |
|||
template <bool IsShort> |
|||
friend bool operator==( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
return is_eq(a, b); |
|||
} |
|||
template <bool IsShort> |
|||
friend bool operator!=( |
|||
const digest_position_array_base& a, |
|||
const digest_data<IsAlphabetRestricted, IsShort>& b |
|||
) noexcept |
|||
{ |
|||
return !is_eq(a, b); |
|||
} |
|||
template <bool IsShort> |
|||
friend bool operator==( |
|||
const digest_data<IsAlphabetRestricted, IsShort>& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
return is_eq(b, a); |
|||
} |
|||
template <bool IsShort> |
|||
friend bool operator!=( |
|||
const digest_data<IsAlphabetRestricted, IsShort>& a, |
|||
const digest_position_array_base& b |
|||
) noexcept |
|||
{ |
|||
return !is_eq(b, a); |
|||
} |
|||
|
|||
// Friend classes
|
|||
template <comparison_version> friend class internal::digest_comparison_base; |
|||
template <comparison_version> friend class digest_comparison; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
rolling_hash.hpp |
|||
Rolling hash implementation (without known bugs) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_ROLLING_HASH_HPP |
|||
#define FFUZZYPP_ROLLING_HASH_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
class rolling_hash |
|||
{ |
|||
public: |
|||
static constexpr const uint_least32_t window_size = 7; |
|||
static_assert(window_size != 0, "window_size must not be zero."); |
|||
protected: |
|||
uint_least32_t h1, h2, h3; |
|||
uint_least32_t n; |
|||
unsigned char window[window_size]; |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
h1 = h2 = h3 = n = 0; |
|||
for (size_t i = 0; i < window_size; i++) |
|||
window[i] = 0; |
|||
} |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
uint_least32_t C(c); |
|||
h2 = h2 - h1 + window_size * C; |
|||
h1 = h1 + C - window[n]; |
|||
h3 = (h3 << 5) ^ C; |
|||
window[n] = c; |
|||
n++; |
|||
if (n == window_size) |
|||
n = 0; |
|||
} |
|||
uint_least32_t sum(void) const noexcept |
|||
{ |
|||
return (h1 + h2 + h3) & uint_least32_t(0xfffffffful); |
|||
} |
|||
public: |
|||
rolling_hash(void) noexcept |
|||
{ |
|||
reset(); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,87 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
rolling_hash_ssdeep.hpp |
|||
Rolling hash implementation (with known bug in ssdeep) |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_ROLLING_HASH_SSDEEP_HPP |
|||
#define FFUZZYPP_ROLLING_HASH_SSDEEP_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include "rolling_hash.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
|
|||
// rolling_hash with known rolling-hash bug on ssdeep
|
|||
class rolling_hash_ssdeep |
|||
{ |
|||
public: |
|||
static constexpr const uint_least32_t window_size = rolling_hash::window_size; |
|||
private: |
|||
uint_least32_t h1, h2, h3; |
|||
uint_least32_t n; |
|||
unsigned char window[window_size]; |
|||
public: |
|||
void update(unsigned char c) noexcept |
|||
{ |
|||
// emulate bug on ssdeep
|
|||
uint_least32_t C(c); |
|||
h2 = h2 - h1 + window_size * C; |
|||
h1 = h1 + C - window[n % window_size]; |
|||
h3 = (h3 << 5) ^ C; |
|||
window[n % window_size] = c; |
|||
n++; |
|||
if ((n & uint_least32_t(0xfffffffful)) == 0) |
|||
n = 0; |
|||
} |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
h1 = h2 = h3 = n = 0; |
|||
for (size_t i = 0; i < window_size; i++) |
|||
window[i] = 0; |
|||
} |
|||
uint_least32_t sum(void) const noexcept |
|||
{ |
|||
return (h1 + h2 + h3) & uint_least32_t(0xfffffffful); |
|||
} |
|||
public: |
|||
rolling_hash_ssdeep(void) noexcept |
|||
{ |
|||
reset(); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,345 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/common_substr.hpp |
|||
Utility to find common substring in two strings |
|||
|
|||
|
|||
CREDITS OF ORIGINAL VERSION OF SSDEEP |
|||
|
|||
Copyright (C) 2002 Andrew Tridgell <tridge@samba.org> |
|||
Copyright (C) 2006 ManTech International Corporation |
|||
Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de> |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
|
|||
CREDIT OF MODIFIED PORTIONS (HASHARRAY ALGORITHM) |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
CREDIT OF ADDED PORTIONS (BIT-PARALLEL ALGORITHM) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_COMMON_SUBSTR_HPP |
|||
#define FFUZZYPP_STRINGS_COMMON_SUBSTR_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
#include <cstring> |
|||
|
|||
#include <limits> |
|||
#include <type_traits> |
|||
|
|||
#include "../rolling_hash.hpp" |
|||
#include "position_array.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <size_t SubstrSize> |
|||
class common_substr_hasharray_impl |
|||
{ |
|||
private: |
|||
common_substr_hasharray_impl(void) = delete; |
|||
common_substr_hasharray_impl(const common_substr_hasharray_impl&) = delete; |
|||
public: |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size >= rolling_hash::window_size, |
|||
"substr_size must be equal or greater than window_size."); |
|||
public: |
|||
static bool match_long_buf( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len, |
|||
uint_least32_t* hashes_buf |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1); |
|||
assert(s2); |
|||
assert(s1len >= substr_size); |
|||
assert(s2len >= substr_size); |
|||
#endif |
|||
rolling_hash r; |
|||
// compute rolling hashes for each index of s1
|
|||
for (size_t i = 0; i < substr_size - 1; i++) |
|||
r.update(static_cast<unsigned char>(s1[i])); |
|||
for (size_t i = substr_size - 1; i < s1len; i++) |
|||
{ |
|||
r.update(static_cast<unsigned char>(s1[i])); |
|||
hashes_buf[i - (substr_size - 1)] = r.sum(); |
|||
} |
|||
s1len -= (substr_size - 1); |
|||
// compute rolling hashes for each index of s2
|
|||
r.reset(); |
|||
for (size_t j = 0; j < substr_size - 1; j++) |
|||
r.update(static_cast<unsigned char>(s2[j])); |
|||
for (size_t j = 0; j < s2len - (substr_size - 1); j++) |
|||
{ |
|||
r.update(static_cast<unsigned char>(s2[j + (substr_size - 1)])); |
|||
uint_least32_t h = r.sum(); |
|||
for (size_t i = 0; i < s1len; i++) |
|||
{ |
|||
// make sure we actually have common substring if hash matches
|
|||
if (hashes_buf[i] == h && !memcmp(s1 + i, s2 + j, substr_size)) |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
template < |
|||
size_t SubstrSize, |
|||
size_t MaxSize = std::numeric_limits<size_t>::max() |
|||
> |
|||
class common_substr_bitparallel_impl |
|||
{ |
|||
private: |
|||
common_substr_bitparallel_impl(void) = delete; |
|||
common_substr_bitparallel_impl(const common_substr_bitparallel_impl&) = delete; |
|||
public: |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(0 < max_size, "max_size must be nonzero."); |
|||
public: |
|||
template <typename TBitmap, char CMin, char CMax> |
|||
static bool match( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typedef position_array<TBitmap, char, CMin, CMax> pa_type; |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s2); |
|||
assert(s2len >= substr_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
// position array is too short to have such substrings
|
|||
if (pa_type::max_strlen < substr_size) |
|||
return false; |
|||
size_t r = substr_size - 1; |
|||
while (true) |
|||
{ |
|||
size_t l = r - (substr_size - 1); |
|||
// we must reverse s2 because bitmap of s1 is reversed.
|
|||
const char* ch = &s2[s2len - 1 - r]; |
|||
TBitmap D = s1[*ch]; |
|||
while (D) |
|||
{ |
|||
r--; |
|||
D = (D << 1) & s1[*++ch]; |
|||
if (r == l && D) |
|||
return true; |
|||
} |
|||
// Boyer-Moore-like skipping
|
|||
if (max_size - 1 <= std::numeric_limits<size_t>::max() - substr_size) |
|||
{ |
|||
// (max_size - 1) + substr_size <= max(size_t)
|
|||
r += substr_size; |
|||
if (r >= s2len) |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
// (max_size - 1) + substr_size > max(size_t)
|
|||
// which means we cannot safely perform r += substr_size first (very unlikely)
|
|||
if (r >= s2len - substr_size) |
|||
break; |
|||
r += substr_size; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
template <size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_hasharray |
|||
{ |
|||
private: |
|||
common_substr_hasharray(void) = delete; |
|||
common_substr_hasharray(const common_substr_hasharray&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size <= max_size, "substring size must not be greater than the maximum size."); |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1); |
|||
assert(s2); |
|||
assert(s1len <= max_size); |
|||
#endif |
|||
uint_least32_t hashes[max_size - (substr_size - 1)]; |
|||
if (s1len < substr_size) |
|||
return false; |
|||
if (s2len < substr_size) |
|||
return false; |
|||
return internal |
|||
::common_substr_hasharray_impl<substr_size> |
|||
::match_long_buf(s1, s1len, s2, s2len, hashes); |
|||
} |
|||
}; |
|||
|
|||
template <typename TBitmap, char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_bitparallel |
|||
{ |
|||
private: |
|||
common_substr_bitparallel(void) = delete; |
|||
common_substr_bitparallel(const common_substr_bitparallel&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size <= max_size, "substring size must not be greater than the maximum size."); |
|||
static_assert(max_size <= position_array<TBitmap, char, CMin, CMax>::max_strlen, |
|||
"max_size must not be greater than max_strlen of corresponding position_matrix for efficiency."); |
|||
public: |
|||
static bool match( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s2); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
if (s2len < substr_size) |
|||
return false; |
|||
return internal |
|||
::common_substr_bitparallel_impl<substr_size, max_size> |
|||
::match(s1, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename TBitmap, char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_bitparallel_wrapper |
|||
{ |
|||
private: |
|||
common_substr_bitparallel_wrapper(void) = delete; |
|||
common_substr_bitparallel_wrapper(const common_substr_bitparallel_wrapper&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
static_assert(0 < substr_size, "substr_size must be nonzero."); |
|||
static_assert(substr_size <= max_size, "substring size must not be greater than the maximum size."); |
|||
static_assert(max_size <= position_array<TBitmap, char, CMin, CMax>::max_strlen, |
|||
"max_size must not be greater than max_strlen of corresponding position_matrix for efficiency."); |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s2); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
if (s2len < substr_size) |
|||
return false; |
|||
position_array<TBitmap, char, CMin, CMax> parray(s1, s1len); |
|||
return internal |
|||
::common_substr_bitparallel_impl<substr_size, max_size> |
|||
::match(parray, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <char CMin, char CMax, size_t MaxSize, size_t SubstrSize, bool IsPositionArrayAvailable> |
|||
class common_substr_impl_selector; |
|||
|
|||
template <char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_impl_selector<CMin, CMax, MaxSize, SubstrSize, true> |
|||
{ |
|||
private: |
|||
common_substr_impl_selector(void) = delete; |
|||
common_substr_impl_selector(const common_substr_impl_selector&) = delete; |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typename auto_position_array<MaxSize, char, CMin, CMax>::type parray(s1, s1len); |
|||
return common_substr_bitparallel<typename decltype(parray)::bitmap_type, |
|||
CMin, CMax, MaxSize, SubstrSize>::match(parray, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <char CMin, char CMax, size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_impl_selector<CMin, CMax, MaxSize, SubstrSize, false> |
|||
{ |
|||
private: |
|||
common_substr_impl_selector(void) = delete; |
|||
common_substr_impl_selector(const common_substr_impl_selector&) = delete; |
|||
public: |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return common_substr_hasharray<MaxSize, SubstrSize>::match(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
|
|||
|
|||
template <size_t MaxSize, size_t SubstrSize> |
|||
class common_substr_fast |
|||
{ |
|||
private: |
|||
common_substr_fast(void) = delete; |
|||
common_substr_fast(const common_substr_fast&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static constexpr const size_t substr_size = SubstrSize; |
|||
public: |
|||
template < |
|||
char CMin = std::numeric_limits<char>::min(), |
|||
char CMax = std::numeric_limits<char>::max() |
|||
> |
|||
static bool match( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return internal::common_substr_impl_selector< |
|||
CMin, CMax, max_size, substr_size, |
|||
is_auto_position_array_available<MaxSize, char, CMin, CMax>::value |
|||
>::match(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,521 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/edit_dist.hpp |
|||
Indel distance implementation |
|||
|
|||
|
|||
Copyright (C) 2014 kikairoya <kikairoya@gmail.com> |
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
This program can be used, redistributed or modified under any of |
|||
Boost Software License 1.0, GPL v2 or GPL v3 |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_EDIT_DIST_HPP |
|||
#define FFUZZYPP_STRINGS_EDIT_DIST_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
|
|||
#include <algorithm> |
|||
#include <limits> |
|||
#include <utility> |
|||
|
|||
#include "../utils/safe_int.hpp" |
|||
#include "position_array.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename Tcost> |
|||
class edit_dist_dp_impl |
|||
{ |
|||
private: |
|||
edit_dist_dp_impl(void) = delete; |
|||
edit_dist_dp_impl(const edit_dist_dp_impl&) = delete; |
|||
public: |
|||
typedef Tcost cost_type; |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<cost_type>(), |
|||
"cost_type must be an unsigned integral type."); |
|||
private: |
|||
static void update_cost_inner( |
|||
const char* s1, |
|||
const char* s2, size_t s2len, |
|||
size_t i, |
|||
cost_type* &row1, cost_type* &row2 |
|||
) noexcept |
|||
{ |
|||
row2[0] = static_cast<cost_type>(i) + 1; |
|||
for (size_t j = 0; j < s2len; j++) |
|||
{ |
|||
cost_type cost_a = row1[j+1] + 1; |
|||
cost_type cost_d = row2[j] + 1; |
|||
cost_type cost_r = row1[j] + (s1[i] == s2[j] ? 0 : 2); |
|||
row2[j+1] = std::min(std::min(cost_a, cost_d), cost_r); |
|||
} |
|||
std::swap(row1, row2); |
|||
} |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len, |
|||
cost_type* row1, cost_type* row2 |
|||
) noexcept |
|||
{ |
|||
for (size_t j = 0; j <= s2len; j++) |
|||
row1[j] = static_cast<cost_type>(j); |
|||
for (size_t i = 0; i < s1len; i++) |
|||
update_cost_inner(s1, s2, s2len, i, row1, row2); |
|||
return row1[s2len]; |
|||
} |
|||
static cost_type cost_nonempty( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len, |
|||
cost_type* row1, cost_type* row2 |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len > 0); |
|||
assert(s2len > 0); |
|||
#endif |
|||
row1[0] = 1; |
|||
for (size_t j = 0; j < s2len; j++) |
|||
{ |
|||
cost_type cost_d = row1[j] + 1; |
|||
cost_type cost_r = static_cast<cost_type>(j) + (s1[0] == s2[j] ? 0 : 2); |
|||
row1[j+1] = std::min(cost_d, cost_r); |
|||
} |
|||
for (size_t i = 1; i < s1len; i++) |
|||
update_cost_inner(s1, s2, s2len, i, row1, row2); |
|||
return row1[s2len]; |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_bitparallel_impl |
|||
{ |
|||
private: |
|||
edit_dist_bitparallel_impl(void) = delete; |
|||
edit_dist_bitparallel_impl(const edit_dist_bitparallel_impl&) = delete; |
|||
public: |
|||
typedef Tcost cost_type; |
|||
public: |
|||
template <typename TBitmap, char CMin, char CMax> |
|||
static cost_type cost_nonempty( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
cost_type cur = s1len; |
|||
TBitmap msb = TBitmap(1ull) << (s1len - 1); |
|||
TBitmap pv = -1; |
|||
TBitmap nv = 0; |
|||
for (size_t i = 0; i < s2len; i++) |
|||
{ |
|||
TBitmap mt = s1[s2[i]]; |
|||
TBitmap zd = (((mt & pv) + pv) ^ pv) | mt | nv; |
|||
TBitmap nh = pv & zd; |
|||
if (nh & msb) |
|||
--cur; |
|||
TBitmap x = nv | ~(pv | zd) | (pv & ~mt & TBitmap(1ull)); |
|||
TBitmap y = (pv - nh) >> 1; |
|||
/*
|
|||
i-th bit of ph does not depend on i-th bit of y |
|||
(only upper bits of ph are affected). |
|||
So, ph does not depend on invalid bit in y. |
|||
*/ |
|||
TBitmap ph = (x + y) ^ y; |
|||
if (ph & msb) |
|||
++cur; |
|||
TBitmap t = (ph << 1) + TBitmap(1ull); |
|||
nv = t & zd; |
|||
pv = (nh << 1) | ~(t | zd) | (t & (pv - nh)); |
|||
} |
|||
return cur; |
|||
} |
|||
template <typename TBitmap, char CMin, char CMax> |
|||
static cost_type cost( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return s1len == 0 |
|||
? s2len |
|||
: cost_nonempty(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_dp |
|||
{ |
|||
private: |
|||
edit_dist_dp(void) = delete; |
|||
edit_dist_dp(const edit_dist_dp&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
cost_type rows[2][max_size + 1]; |
|||
return internal::edit_dist_dp_impl<cost_type>::cost(s1, s1len, s2, s2len, rows[0], rows[1]); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_nonempty_dp |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_dp(void) = delete; |
|||
edit_dist_nonempty_dp(const edit_dist_nonempty_dp&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
cost_type rows[2][max_size + 1]; |
|||
return internal::edit_dist_dp_impl<cost_type>::cost_nonempty(s1, s1len, s2, s2len, rows[0], rows[1]); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_bitparallel |
|||
{ |
|||
private: |
|||
edit_dist_bitparallel(void) = delete; |
|||
edit_dist_bitparallel(const edit_dist_bitparallel&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_bitparallel_wrapper |
|||
{ |
|||
private: |
|||
edit_dist_bitparallel_wrapper(void) = delete; |
|||
edit_dist_bitparallel_wrapper(const edit_dist_bitparallel_wrapper&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
position_array<TBitmap, char, CMin, CMax> parray(s1, s1len); |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost(parray, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_nonempty_bitparallel |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_bitparallel(void) = delete; |
|||
edit_dist_nonempty_bitparallel(const edit_dist_nonempty_bitparallel&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const position_array<TBitmap, char, CMin, CMax>& s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost_nonempty(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, typename TBitmap, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_nonempty_bitparallel_wrapper |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_bitparallel_wrapper(void) = delete; |
|||
edit_dist_nonempty_bitparallel_wrapper(const edit_dist_nonempty_bitparallel_wrapper&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
static_assert(max_size > 0, "max_size must not be zero."); |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, max_size>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::is_valid, |
|||
"max_size + 1 must be in range of size_t."); |
|||
typedef Tcost cost_type; |
|||
static_assert(safe_int::safe_mul< |
|||
safe_int::uvalue<cost_type, max_size>, |
|||
safe_int::uvalue<cost_type, 2> |
|||
>::is_valid, |
|||
"max_size * 2 must be in range of cost_type."); |
|||
public: |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(s1len <= max_size); |
|||
assert(s2len <= max_size); |
|||
#endif |
|||
position_array<TBitmap, char, CMin, CMax> parray(s1, s1len); |
|||
return internal::edit_dist_bitparallel_impl<Tcost, MaxSize>::cost_nonempty(parray, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename Tcost, char CMin, char CMax, size_t MaxSize, bool IsPositionArrayAvailable> |
|||
class edit_dist_impl_selector; |
|||
|
|||
template <typename Tcost, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_impl_selector<Tcost, CMin, CMax, MaxSize, true> |
|||
{ |
|||
private: |
|||
edit_dist_impl_selector(void) = delete; |
|||
edit_dist_impl_selector(const edit_dist_impl_selector&) = delete; |
|||
public: |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typename auto_position_array<MaxSize, char, CMin, CMax>::type parray(s1, s1len); |
|||
return edit_dist_bitparallel<Tcost, |
|||
typename decltype(parray)::bitmap_type, CMin, CMax, MaxSize>::cost(parray, s1len, s2, s2len); |
|||
} |
|||
static Tcost cost_nonempty( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
typename auto_position_array<MaxSize, char, CMin, CMax>::type parray(s1, s1len); |
|||
return edit_dist_nonempty_bitparallel<Tcost, |
|||
typename decltype(parray)::bitmap_type, CMin, CMax, MaxSize>::cost(parray, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, char CMin, char CMax, size_t MaxSize> |
|||
class edit_dist_impl_selector<Tcost, CMin, CMax, MaxSize, false> |
|||
{ |
|||
private: |
|||
edit_dist_impl_selector(void) = delete; |
|||
edit_dist_impl_selector(const edit_dist_impl_selector&) = delete; |
|||
public: |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return edit_dist_dp<Tcost, MaxSize>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
static Tcost cost_nonempty( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return edit_dist_nonempty_dp<Tcost, MaxSize>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
|
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_fast |
|||
{ |
|||
private: |
|||
edit_dist_fast(void) = delete; |
|||
edit_dist_fast(const edit_dist_fast&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
typedef Tcost cost_type; |
|||
public: |
|||
template < |
|||
char CMin = std::numeric_limits<char>::min(), |
|||
char CMax = std::numeric_limits<char>::max() |
|||
> |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return internal::edit_dist_impl_selector< |
|||
cost_type, CMin, CMax, max_size, |
|||
is_auto_position_array_available<MaxSize, char, CMin, CMax>::value |
|||
>::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tcost, size_t MaxSize> |
|||
class edit_dist_nonempty_fast |
|||
{ |
|||
private: |
|||
edit_dist_nonempty_fast(void) = delete; |
|||
edit_dist_nonempty_fast(const edit_dist_nonempty_fast&) = delete; |
|||
public: |
|||
static constexpr const size_t max_size = MaxSize; |
|||
typedef Tcost cost_type; |
|||
public: |
|||
template < |
|||
char CMin = std::numeric_limits<char>::min(), |
|||
char CMax = std::numeric_limits<char>::max() |
|||
> |
|||
static Tcost cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
return internal::edit_dist_impl_selector< |
|||
cost_type, CMin, CMax, max_size, |
|||
is_auto_position_array_available<MaxSize, char, CMin, CMax>::value |
|||
>::cost_nonempty(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
template <typename Tedit_dist> |
|||
class edit_dist_norm |
|||
{ |
|||
private: |
|||
edit_dist_norm(void) = delete; |
|||
edit_dist_norm(const edit_dist_norm&) = delete; |
|||
public: |
|||
typedef typename Tedit_dist::cost_type cost_type; |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
// Normalization: make s1 short for better effficiency
|
|||
if (s1len <= s2len) |
|||
return Tedit_dist::cost(s1, s1len, s2, s2len); |
|||
else |
|||
return Tedit_dist::cost(s2, s2len, s1, s1len); |
|||
} |
|||
}; |
|||
|
|||
template <typename Tedit_dist> |
|||
class edit_dist_norm_rev |
|||
{ |
|||
private: |
|||
edit_dist_norm_rev(void) = delete; |
|||
edit_dist_norm_rev(const edit_dist_norm_rev&) = delete; |
|||
public: |
|||
typedef typename Tedit_dist::cost_type cost_type; |
|||
static cost_type cost( |
|||
const char* s1, size_t s1len, |
|||
const char* s2, size_t s2len |
|||
) noexcept |
|||
{ |
|||
// Reverse normalization: make s2 short for (possibly) worst effficiency
|
|||
if (s1len <= s2len) |
|||
return Tedit_dist::cost(s2, s2len, s1, s1len); |
|||
else |
|||
return Tedit_dist::cost(s1, s1len, s2, s2len); |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,82 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/nosequences.hpp |
|||
String utilities with same interface as sequence utilities |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_NOSEQUENCES_HPP |
|||
#define FFUZZYPP_STRINGS_NOSEQUENCES_HPP |
|||
|
|||
#include <cstddef> |
|||
|
|||
#include "terminators.hpp" |
|||
#include "transform.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
template <typename Ttransform = default_char_transform> |
|||
class nosequences |
|||
{ |
|||
private: |
|||
nosequences(void) = delete; |
|||
nosequences(const nosequences&) = delete; |
|||
public: |
|||
static constexpr bool has_sequences(const char* buf, size_t size) noexcept |
|||
{ |
|||
return false; |
|||
} |
|||
static void copy_raw(char* out, const char* in, size_t size) noexcept |
|||
{ |
|||
while (size--) |
|||
*out++ = Ttransform::transform(*in++); |
|||
} |
|||
static size_t copy_elim_sequences(char* out, const char* in, size_t size) noexcept |
|||
{ |
|||
copy_raw(out, in, size); |
|||
return size; |
|||
} |
|||
template <char... terms> |
|||
class string_copy |
|||
{ |
|||
private: |
|||
string_copy(void) = delete; |
|||
string_copy(const string_copy&) = delete; |
|||
public: |
|||
static bool copy_elim_sequences(char*& out, size_t outsize, const char*& in) noexcept |
|||
{ |
|||
while (outsize--) |
|||
{ |
|||
char ch; |
|||
if (terminators<terms...>::isterm(ch = *in)) |
|||
return true; |
|||
in++; |
|||
*out++ = Ttransform::transform(ch); |
|||
} |
|||
if (terminators<terms...>::isterm(*in)) |
|||
return true; |
|||
return false; |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,409 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/position_array.hpp |
|||
Position array (for bit-parallel algorithms) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_POSITION_ARRAY_HPP |
|||
#define FFUZZYPP_STRINGS_POSITION_ARRAY_HPP |
|||
|
|||
#include <cassert> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
#include <cstring> |
|||
|
|||
#include <limits> |
|||
#include <type_traits> |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename T> |
|||
class digits_on_size |
|||
{ |
|||
private: |
|||
digits_on_size(void) = delete; |
|||
digits_on_size(const digits_on_size&) = delete; |
|||
public: |
|||
static constexpr const size_t size = |
|||
std::numeric_limits<T>::digits <= std::numeric_limits<size_t>::max() |
|||
? std::numeric_limits<T>::digits |
|||
: std::numeric_limits<size_t>::max(); |
|||
}; |
|||
|
|||
/*
|
|||
Template to return guaranteed bit length on unsigned integral types: |
|||
General template works only for binary (radix==2) types and |
|||
specialized templates work for radix!=2 binary types. |
|||
*/ |
|||
template <typename T> |
|||
class guaranteed_bitlen |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
static_assert(std::is_integral<T>::value, "T must be an integral type."); |
|||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned integral type."); |
|||
static_assert(std::numeric_limits<T>::radix == 2, "T must be a binary unsigned integral type."); |
|||
public: |
|||
static constexpr const size_t size = digits_on_size<T>::size; |
|||
}; |
|||
|
|||
template <> |
|||
class guaranteed_bitlen<unsigned> |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
typedef std::numeric_limits<unsigned> limits_type; |
|||
public: |
|||
static constexpr const size_t size = |
|||
limits_type::radix == 2 && limits_type::digits < 16 |
|||
? digits_on_size<unsigned>::size : 16; |
|||
}; |
|||
|
|||
template <> |
|||
class guaranteed_bitlen<unsigned long> |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
typedef std::numeric_limits<unsigned long> limits_type; |
|||
public: |
|||
static constexpr const size_t size = |
|||
limits_type::radix == 2 && limits_type::digits < 32 |
|||
? digits_on_size<unsigned long>::size : 32; |
|||
}; |
|||
|
|||
template <> |
|||
class guaranteed_bitlen<unsigned long long> |
|||
{ |
|||
private: |
|||
guaranteed_bitlen(void) = delete; |
|||
guaranteed_bitlen(const guaranteed_bitlen&) = delete; |
|||
private: |
|||
typedef std::numeric_limits<unsigned long long> limits_type; |
|||
public: |
|||
static constexpr const size_t size = |
|||
limits_type::radix == 2 && limits_type::digits < 64 |
|||
? digits_on_size<unsigned long long>::size : 64; |
|||
}; |
|||
} |
|||
|
|||
class position_array_params |
|||
{ |
|||
private: |
|||
position_array_params(void) = delete; |
|||
position_array_params(const position_array_params&) = delete; |
|||
public: |
|||
#ifdef FFUZZYPP_DISABLE_POSITION_ARRAY |
|||
static constexpr const bool is_disabled_by_build = true; |
|||
#else |
|||
static constexpr const bool is_disabled_by_build = false; |
|||
#endif |
|||
static constexpr const size_t max_efficient_array_size = 1024; |
|||
}; |
|||
static_assert(position_array_params::max_efficient_array_size >= 64, |
|||
"position_array_params::max_efficient_array_size must not be less than 64."); |
|||
|
|||
template < |
|||
typename TBitmap, |
|||
typename TChar, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
class position_array_safety |
|||
{ |
|||
private: |
|||
position_array_safety(void) = delete; |
|||
position_array_safety(const position_array_safety&) = delete; |
|||
public: |
|||
static constexpr const bool is_welldefined = |
|||
std::is_integral<TBitmap>::value && |
|||
std::is_unsigned<TBitmap>::value && |
|||
std::is_same<TBitmap, decltype(TBitmap(0u) | TBitmap(0u))>::value && |
|||
std::is_integral<TChar>::value && |
|||
(std::numeric_limits<TBitmap>::radix == 2 || |
|||
std::is_same<TBitmap, unsigned>::value || |
|||
std::is_same<TBitmap, unsigned long>::value || |
|||
std::is_same<TBitmap, unsigned long long>::value |
|||
) && |
|||
(CMin <= CMax) && |
|||
(std::is_unsigned<TChar>::value |
|||
? CMax - CMin <= std::numeric_limits<size_t>::max() |
|||
: (0 <= CMin |
|||
? std::numeric_limits<intmax_t>::min() + intmax_t(CMin) <= intmax_t(CMax) |
|||
: intmax_t(CMax) <= std::numeric_limits<intmax_t>::max() + intmax_t(CMin) |
|||
) && intmax_t(CMax) - intmax_t(CMin) <= std::numeric_limits<size_t>::max() |
|||
) && |
|||
(size_t(CMax) - size_t(CMin) < std::numeric_limits<size_t>::max()) && |
|||
(sizeof(TBitmap) <= std::numeric_limits<size_t>::max() / (size_t(CMax) - size_t(CMin) + 1u)); |
|||
public: |
|||
static constexpr const bool is_considered_efficient = |
|||
!position_array_params::is_disabled_by_build && is_welldefined && |
|||
(size_t(CMax) - size_t(CMin) + 1u) <= position_array_params::max_efficient_array_size; |
|||
}; |
|||
|
|||
template < |
|||
typename TBitmap = unsigned long long, |
|||
typename TChar = char, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
class position_array |
|||
{ |
|||
private: |
|||
static_assert(std::is_integral<TBitmap>::value, "TBitmap must be an integral type."); |
|||
static_assert(std::is_unsigned<TBitmap>::value, "TBitmap must be an unsigned integral type."); |
|||
static_assert(std::is_same<TBitmap, decltype(TBitmap(0u) | TBitmap(0u))>::value, |
|||
"TBitmap must preserve its type after integral promotion."); |
|||
static_assert(std::is_integral<TChar>::value, "TChar must be an integral type."); |
|||
static_assert(CMin <= CMax, "CMin must not be greater than CMax."); |
|||
static_assert( |
|||
/*
|
|||
Because signed to unsigned conversion is well-defined, |
|||
this predicate also indicates whether size_t(CMax) - size_t(CMin) is valid. |
|||
Note that this predicate does not cover all the case where |
|||
char is signed and (CMin - CMax) is greater than the maximum value of intmax_t. |
|||
*/ |
|||
std::is_unsigned<TChar>::value |
|||
? CMax - CMin <= std::numeric_limits<size_t>::max() |
|||
: (0 <= CMin |
|||
? std::numeric_limits<intmax_t>::min() + intmax_t(CMin) <= intmax_t(CMax) |
|||
: intmax_t(CMax) <= std::numeric_limits<intmax_t>::max() + intmax_t(CMin) |
|||
) && intmax_t(CMax) - intmax_t(CMin) <= std::numeric_limits<size_t>::max(), |
|||
"(CMax - CMin) must be in range of both size_t and the widest type with same signedness as TChar."); |
|||
static_assert(size_t(CMax) - size_t(CMin) < std::numeric_limits<size_t>::max(), |
|||
"(size_t(CMax) - size_t(CMin) + 1) must be in range of size_t."); |
|||
static_assert(sizeof(TBitmap) <= std::numeric_limits<size_t>::max() / (size_t(CMax) - size_t(CMin) + 1u), |
|||
"(array_size * sizeof(TBitmap)) must be in range of size_t."); |
|||
public: |
|||
static constexpr const size_t array_size = size_t(CMax) - size_t(CMin) + 1u; |
|||
static constexpr const size_t max_strlen = internal::guaranteed_bitlen<TBitmap>::size; |
|||
typedef TBitmap bitmap_type; |
|||
typedef TChar char_type; |
|||
static constexpr const char_type char_min = CMin; |
|||
static constexpr const char_type char_max = CMax; |
|||
private: |
|||
bitmap_type bitmap[array_size]; |
|||
public: |
|||
void reset(void) noexcept |
|||
{ |
|||
memset(bitmap, 0, sizeof(bitmap)); |
|||
} |
|||
void construct_noinit(const char_type* str, size_t len) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(len <= max_strlen); |
|||
#endif |
|||
for (size_t i = 0; i < len; i++) |
|||
{ |
|||
char_type ch = *str++; |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(char_min <= ch && ch <= char_max); |
|||
#endif |
|||
bitmap[size_t(ch) - size_t(char_min)] |= bitmap_type(1u) << i; |
|||
} |
|||
} |
|||
void construct_noinit_safe(const char_type* str, size_t len) noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(len <= max_strlen); |
|||
#endif |
|||
for (size_t i = 0; i < len; i++) |
|||
{ |
|||
char_type ch = *str++; |
|||
if (ch < char_min || char_max < ch) |
|||
continue; |
|||
bitmap[size_t(ch) - size_t(char_min)] |= bitmap_type(1u) << i; |
|||
} |
|||
} |
|||
void construct(const char_type* str, size_t len) noexcept |
|||
{ |
|||
reset(); |
|||
construct_noinit(str, len); |
|||
} |
|||
void construct_safe(const char_type* str, size_t len) noexcept |
|||
{ |
|||
reset(); |
|||
construct_noinit_safe(str, len); |
|||
} |
|||
public: |
|||
bitmap_type bitmap_for(char_type ch) const noexcept |
|||
{ |
|||
#ifdef FFUZZYPP_DEBUG |
|||
assert(char_min <= ch && ch <= char_max); |
|||
#endif |
|||
return bitmap[size_t(ch) - size_t(char_min)]; |
|||
} |
|||
bitmap_type bitmap_for_safe(char_type ch) const noexcept |
|||
{ |
|||
if (ch < char_min || char_max < ch) |
|||
return bitmap_type(0u); |
|||
return bitmap[size_t(ch) - size_t(char_min)]; |
|||
} |
|||
bitmap_type operator[](char_type ch) const noexcept |
|||
{ |
|||
return bitmap_for(ch); |
|||
} |
|||
const bitmap_type* bitmap_data(void) const noexcept |
|||
{ |
|||
return bitmap; |
|||
} |
|||
public: |
|||
position_array(void) noexcept = default; // initialize to undefined state
|
|||
position_array(const position_array& other) noexcept |
|||
{ |
|||
if (bitmap != other.bitmap) |
|||
memcpy(bitmap, other.bitmap, sizeof(bitmap)); |
|||
} |
|||
const position_array& operator=(const position_array& other) noexcept |
|||
{ |
|||
if (bitmap != other.bitmap) |
|||
memcpy(bitmap, other.bitmap, sizeof(bitmap)); |
|||
return *this; |
|||
} |
|||
position_array(const char_type* str, size_t len) noexcept |
|||
{ |
|||
construct(str, len); |
|||
} |
|||
position_array(const char_type* str, size_t len, bool safe) noexcept |
|||
{ |
|||
if (safe) |
|||
construct_safe(str, len); |
|||
else |
|||
construct(str, len); |
|||
} |
|||
static void swap(position_array& a, position_array& b) noexcept |
|||
{ |
|||
std::swap(a.bitmap, b.bitmap); // C++11 version of swap
|
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax, typename TEnabler = void> |
|||
class auto_position_array_internal; |
|||
|
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_internal<MaxSize, TChar, CMin, CMax, typename std::enable_if<( |
|||
MaxSize > 0 && MaxSize <= 16 && position_array_safety<unsigned, TChar, CMin, CMax>::is_considered_efficient |
|||
)>::type> |
|||
{ |
|||
private: |
|||
auto_position_array_internal(void); |
|||
auto_position_array_internal(const auto_position_array_internal&) = delete; |
|||
public: |
|||
typedef unsigned int_type; |
|||
}; |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_internal<MaxSize, TChar, CMin, CMax, typename std::enable_if<( |
|||
MaxSize > 16 && MaxSize <= 32 && position_array_safety<unsigned long, TChar, CMin, CMax>::is_considered_efficient |
|||
)>::type> |
|||
{ |
|||
private: |
|||
auto_position_array_internal(void); |
|||
auto_position_array_internal(const auto_position_array_internal&) = delete; |
|||
public: |
|||
typedef unsigned long int_type; |
|||
}; |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_internal<MaxSize, TChar, CMin, CMax, typename std::enable_if<( |
|||
MaxSize > 32 && MaxSize <= 64 && position_array_safety<unsigned long long, TChar, CMin, CMax>::is_considered_efficient |
|||
)>::type> |
|||
{ |
|||
private: |
|||
auto_position_array_internal(void); |
|||
auto_position_array_internal(const auto_position_array_internal&) = delete; |
|||
public: |
|||
typedef unsigned long long int_type; |
|||
}; |
|||
} |
|||
|
|||
// Predicate to test if automatically-chosen position array available
|
|||
template < |
|||
size_t MaxSize, |
|||
typename TChar = char, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
struct is_auto_position_array_available |
|||
: std::integral_constant<bool, ( |
|||
(MaxSize > 0 && MaxSize <= 16 && position_array_safety<unsigned, TChar, CMin, CMax>::is_considered_efficient) || |
|||
(MaxSize > 16 && MaxSize <= 32 && position_array_safety<unsigned long, TChar, CMin, CMax>::is_considered_efficient) || |
|||
(MaxSize > 32 && MaxSize <= 64 && position_array_safety<unsigned long long, TChar, CMin, CMax>::is_considered_efficient) |
|||
)> |
|||
{ }; |
|||
|
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax, bool> |
|||
class auto_position_array_impl; |
|||
|
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_impl<MaxSize, TChar, CMin, CMax, true> |
|||
{ |
|||
private: |
|||
auto_position_array_impl(void) = delete; |
|||
auto_position_array_impl(const auto_position_array_impl&) = delete; |
|||
public: |
|||
typedef typename auto_position_array_internal<MaxSize, TChar, CMin, CMax>::int_type int_type; |
|||
typedef position_array<int_type, TChar, CMin, CMax> type; |
|||
}; |
|||
|
|||
template <size_t MaxSize, typename TChar, TChar CMin, TChar CMax> |
|||
class auto_position_array_impl<MaxSize, TChar, CMin, CMax, false> |
|||
{ |
|||
private: |
|||
auto_position_array_impl(void) = delete; |
|||
auto_position_array_impl(const auto_position_array_impl&) = delete; |
|||
}; |
|||
} |
|||
|
|||
// Automatically-chosen position array
|
|||
template < |
|||
size_t MaxSize, |
|||
typename TChar = char, |
|||
TChar CMin = std::numeric_limits<TChar>::min(), |
|||
TChar CMax = std::numeric_limits<TChar>::max() |
|||
> |
|||
class auto_position_array |
|||
: public internal::auto_position_array_impl< |
|||
MaxSize, TChar, CMin, CMax, |
|||
is_auto_position_array_available<MaxSize, TChar, CMin, CMax>::value |
|||
> |
|||
{ |
|||
private: |
|||
auto_position_array(void) = delete; |
|||
auto_position_array(const auto_position_array&) = delete; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,154 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/sequences.hpp |
|||
Utilities to find/eliminate sequences of same characters |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_SEQUENCES_HPP |
|||
#define FFUZZYPP_STRINGS_SEQUENCES_HPP |
|||
|
|||
#include <cstddef> |
|||
|
|||
#include "terminators.hpp" |
|||
#include "transform.hpp" |
|||
#include "../utils/safe_int.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
template <size_t MaxSequenceSize, typename Ttransform = default_char_transform> |
|||
class sequences |
|||
{ |
|||
static_assert(safe_int::safe_add< |
|||
safe_int::uvalue<size_t, MaxSequenceSize>, |
|||
safe_int::uvalue<size_t, 1> |
|||
>::value, |
|||
"MaxSequenceSize + 1 must be in range of size_t."); |
|||
private: |
|||
sequences(void) = delete; |
|||
sequences(const sequences&) = delete; |
|||
public: |
|||
static constexpr const size_t max_sequence_size = MaxSequenceSize; |
|||
static_assert(max_sequence_size != 0, "max_sequence_size must not be zero."); |
|||
public: |
|||
static bool has_sequences(const char* buf, size_t size) noexcept |
|||
{ |
|||
if (size <= max_sequence_size) |
|||
return false; |
|||
size_t seq = 0; |
|||
char prev = *buf++; |
|||
while (--size) |
|||
{ |
|||
char curr = *buf++; |
|||
if (curr == prev) |
|||
{ |
|||
if (++seq == max_sequence_size) |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
seq = 0; |
|||
prev = curr; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
static size_t copy_elim_sequences(char* out, const char* in, size_t size) noexcept |
|||
{ |
|||
if (size <= max_sequence_size) |
|||
{ |
|||
for (size_t i = 0; i < size; i++) |
|||
*out++ = Ttransform::transform(*in++); |
|||
return size; |
|||
} |
|||
size_t csz = 1; |
|||
size_t seq = 0; |
|||
char prev = *in++; |
|||
*out++ = Ttransform::transform(prev); |
|||
while (--size) |
|||
{ |
|||
char curr = *in++; |
|||
if (curr == prev) |
|||
{ |
|||
if (++seq >= max_sequence_size) |
|||
{ |
|||
seq = max_sequence_size; |
|||
continue; |
|||
} |
|||
*out++ = Ttransform::transform(curr); csz++; |
|||
} |
|||
else |
|||
{ |
|||
*out++ = Ttransform::transform(curr); csz++; |
|||
seq = 0; prev = curr; |
|||
} |
|||
} |
|||
return csz; |
|||
} |
|||
template <char... terms> |
|||
class string_copy |
|||
{ |
|||
private: |
|||
string_copy(void) = delete; |
|||
string_copy(const string_copy&) = delete; |
|||
public: |
|||
static bool copy_elim_sequences(char*& out, size_t outsize, const char*& in) noexcept |
|||
{ |
|||
size_t seq = 0; |
|||
char prev = *in; |
|||
if (terminators<terms...>::isterm(prev)) |
|||
return true; |
|||
if (outsize == 0) |
|||
return false; |
|||
*out++ = Ttransform::transform(prev); in++; outsize--; |
|||
while (true) |
|||
{ |
|||
char curr = *in; |
|||
if (terminators<terms...>::isterm(curr)) |
|||
return true; |
|||
in++; |
|||
if (curr == prev) |
|||
{ |
|||
if (++seq >= max_sequence_size) |
|||
{ |
|||
seq = max_sequence_size; |
|||
continue; |
|||
} |
|||
if (outsize == 0) |
|||
return false; |
|||
*out++ = Ttransform::transform(curr); outsize--; |
|||
} |
|||
else |
|||
{ |
|||
if (outsize == 0) |
|||
return false; |
|||
*out++ = Ttransform::transform(curr); outsize--; |
|||
seq = 0; prev = curr; |
|||
} |
|||
} |
|||
// unreachable
|
|||
return false; |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,58 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/terminators.hpp |
|||
Terminator characters |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_TERMINATORS_HPP |
|||
#define FFUZZYPP_STRINGS_TERMINATORS_HPP |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
template <char...> class terminators; |
|||
template <> |
|||
class terminators<> |
|||
{ |
|||
private: |
|||
terminators(void) = delete; |
|||
terminators(const terminators&) = delete; |
|||
public: |
|||
static bool isterm(char ch) noexcept |
|||
{ |
|||
return !ch; |
|||
} |
|||
}; |
|||
template <char c, char... rem> |
|||
class terminators<c, rem...> |
|||
{ |
|||
private: |
|||
terminators(void) = delete; |
|||
terminators(const terminators&) = delete; |
|||
public: |
|||
static bool isterm(char ch) noexcept |
|||
{ |
|||
return terminators<rem...>::isterm(ch) || ch == c; |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,44 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hashing |
|||
|
|||
strings/transform.hpp |
|||
String transformation (char-to-char mapping) utility |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_STRINGS_TRANSFORM_HPP |
|||
#define FFUZZYPP_STRINGS_TRANSFORM_HPP |
|||
|
|||
namespace ffuzzy { |
|||
namespace strings { |
|||
|
|||
class default_char_transform |
|||
{ |
|||
private: |
|||
default_char_transform(void) = delete; |
|||
default_char_transform(const default_char_transform&) = delete; |
|||
public: |
|||
static constexpr char transform(char ch) noexcept |
|||
{ |
|||
return ch; |
|||
} |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,60 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
likely.hpp |
|||
Branch prediction control macros |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_LIKELY_HPP |
|||
#define FFUZZYPP_UTILS_LIKELY_HPP |
|||
|
|||
#define FFUZZYPP_LIKELY_PORTABLE(x) (!!(x)) |
|||
|
|||
#ifndef FFUZZYPP_DISABLE_COMPILER_BUILTINS |
|||
#if !defined(__GNUC__) |
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#elif __GNUC__ < 2 |
|||
// DISABLE: GNU compiler version < 2.0
|
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#elif __GNUC__ >= 3 |
|||
// ENABLE: GNU compiler version >= 3.0
|
|||
#elif !defined(__GNUC_MINOR__) |
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#elif __GNUC_MINOR__ <= 95 |
|||
// DISABLE: GNU compiler version <= 2.95
|
|||
#define FFUZZYPP_DISABLE_COMPILER_BUILTINS 1 |
|||
#endif |
|||
#endif |
|||
|
|||
#ifdef FFUZZYPP_LIKELY |
|||
#undef FFUZZYPP_LIKELY |
|||
#endif |
|||
#ifdef FFUZZYPP_UNLIKELY |
|||
#undef FFUZZYPP_UNLIKELY |
|||
#endif |
|||
|
|||
#ifdef FFUZZYPP_DISABLE_COMPILER_BUILTINS |
|||
#define FFUZZYPP_LIKELY FFUZZYPP_LIKELY_PORTABLE |
|||
#define FFUZZYPP_UNLIKELY FFUZZYPP_LIKELY_PORTABLE |
|||
#else |
|||
#define FFUZZYPP_LIKELY(x) (__builtin_expect(!!(x), 1)) |
|||
#define FFUZZYPP_UNLIKELY(x) (__builtin_expect(!!(x), 0)) |
|||
#endif |
|||
|
|||
#endif |
@ -0,0 +1,67 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
minmax.hpp |
|||
C++14-compatible minimum / maximum library |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_MINMAX_HPP |
|||
#define FFUZZYPP_UTILS_MINMAX_HPP |
|||
|
|||
/*
|
|||
std::min and std::max in C++11 are not marked constexpr. |
|||
On the other hand, C++14's std::min and std::max are marked constexpr. |
|||
|
|||
This library implements constexpr min and max in C++11 |
|||
(but compatible with C++14). |
|||
|
|||
However, we won't implement initializer_list variant of |
|||
min and max since we aren't using it. |
|||
*/ |
|||
|
|||
namespace ffuzzy { |
|||
namespace minmax { |
|||
|
|||
template <class T> |
|||
inline constexpr const T& min(const T& a, const T& b) |
|||
{ |
|||
return b < a ? b : a; |
|||
} |
|||
|
|||
template <class T, class Compare> |
|||
inline constexpr const T& min(const T& a, const T& b, Compare comp) |
|||
{ |
|||
return comp(b, a) ? b : a; |
|||
} |
|||
|
|||
template <class T> |
|||
inline constexpr const T& max(const T& a, const T& b) |
|||
{ |
|||
return a < b ? b : a; |
|||
} |
|||
|
|||
template <class T, class Compare> |
|||
inline constexpr const T& max(const T& a, const T& b, Compare comp) |
|||
{ |
|||
return comp(a, b) ? b : a; |
|||
} |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,129 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
numeric_digits.hpp |
|||
Construction of numeric digits |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_NUMERIC_DIGITS_HPP |
|||
#define FFUZZYPP_UTILS_NUMERIC_DIGITS_HPP |
|||
|
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
|
|||
#include <limits> |
|||
|
|||
#include "safe_int.hpp" |
|||
#include "static_assert_query.hpp" |
|||
|
|||
namespace ffuzzy { |
|||
namespace numeric_digits { |
|||
|
|||
template <typename T, size_t Base> |
|||
static constexpr size_t in_base(T value) noexcept |
|||
{ |
|||
static_assert( |
|||
safe_int::contracts::is_unsigned_integral_type<T>(), |
|||
"T must be an unsigned integral type."); |
|||
static_assert(Base >= 2, "Base must be at least two."); |
|||
return (value < Base) ? 1 : 1 + in_base<T, Base>(value / Base); |
|||
} |
|||
|
|||
template <typename T> |
|||
static constexpr size_t in_decimal(T value) noexcept |
|||
{ |
|||
return in_base<T, 10>(value); |
|||
} |
|||
|
|||
// Sanity checks
|
|||
#ifdef FFUZZYPP_STATIC_SANITY_CHECKS |
|||
namespace internal |
|||
{ |
|||
static constexpr uintmax_t expull(uintmax_t base, uintmax_t exponent) noexcept |
|||
{ |
|||
return |
|||
exponent == 0 ? 1 : |
|||
base * expull(base, exponent-1); |
|||
} |
|||
static_assert(expull(10, 0) == 1ull, "sanity check for expull(10, 0) failed."); |
|||
static_assert(expull(10, 1) == 10ull, "sanity check for expull(10, 1) failed."); |
|||
static_assert(expull(10, 2) == 100ull, "sanity check for expull(10, 2) failed."); |
|||
static_assert(expull(10, 3) == 1000ull, "sanity check for expull(10, 3) failed."); |
|||
static_assert(expull(10, 4) == 10000ull, "sanity check for expull(10, 4) failed."); |
|||
static_assert(expull(10, 19) == 10000000000000000000ull, "sanity check for expull(10, 19) failed."); |
|||
|
|||
template <typename T> |
|||
struct digits_in_decimal_check |
|||
{ |
|||
private: |
|||
template <uintmax_t i> |
|||
struct case_digit1 |
|||
{ |
|||
static constexpr const bool value = in_decimal<T>(i) == 1; |
|||
}; |
|||
template <uintmax_t i> |
|||
struct case_digit2 |
|||
{ |
|||
static constexpr const bool value = in_decimal<T>(i + 10u) == 2; |
|||
}; |
|||
template <uintmax_t i> |
|||
struct case_many_borders |
|||
{ |
|||
static constexpr const bool value0 = |
|||
std::numeric_limits<T>::max() < (expull(10, i+1) - 1) |
|||
|| in_decimal<T>(T(expull(10, i+1) - 1)) == i + 1; |
|||
static constexpr const bool value1 = |
|||
std::numeric_limits<T>::max() < expull(10, i+1) |
|||
|| in_decimal<T>(T(expull(10, i+1))) == i + 2; |
|||
static constexpr const bool value = value0 && value1; |
|||
}; |
|||
public: |
|||
static constexpr const bool value = |
|||
static_assert_query::is_all<case_digit1, 10>::value && |
|||
static_assert_query::is_all<case_digit2, 90>::value && |
|||
static_assert_query::is_all<case_many_borders, 19>::value; |
|||
}; |
|||
#ifdef FFUZZYPP_LOCAL_CHK |
|||
#error do not define FFUZZYPP_LOCAL_CHK |
|||
#endif |
|||
#define FFUZZYPP_LOCAL_CHK(typ) \ |
|||
static_assert(digits_in_decimal_check<typ>::value, \ |
|||
"sanity checks for digits_in_decimal failed for " #typ ".") |
|||
FFUZZYPP_LOCAL_CHK(unsigned char); |
|||
FFUZZYPP_LOCAL_CHK(unsigned short); |
|||
FFUZZYPP_LOCAL_CHK(unsigned int); |
|||
FFUZZYPP_LOCAL_CHK(unsigned long); |
|||
FFUZZYPP_LOCAL_CHK(unsigned long long); |
|||
FFUZZYPP_LOCAL_CHK(uint_least8_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_least16_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_least32_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_least64_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast8_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast16_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast32_t); |
|||
FFUZZYPP_LOCAL_CHK(uint_fast64_t); |
|||
FFUZZYPP_LOCAL_CHK(uintmax_t); |
|||
FFUZZYPP_LOCAL_CHK(size_t); |
|||
#undef FFUZZYPP_LOCAL_CHK |
|||
} |
|||
#endif |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,116 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
ranges.hpp |
|||
Value range library |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_RANGES_HPP |
|||
#define FFUZZYPP_UTILS_RANGES_HPP |
|||
|
|||
#include <limits> |
|||
|
|||
|
|||
namespace ffuzzy { |
|||
namespace ranges { |
|||
|
|||
/*
|
|||
Inclusive range ([a, b] where a <= b) |
|||
*/ |
|||
template <typename T> |
|||
class inclusive |
|||
{ |
|||
T v_begin; |
|||
T v_end; |
|||
public: |
|||
class iterator |
|||
{ |
|||
private: |
|||
T value; |
|||
T value_end; |
|||
bool is_end; |
|||
private: |
|||
constexpr iterator( |
|||
T value, |
|||
T value_end, |
|||
bool is_end = false |
|||
) noexcept |
|||
: value(value) |
|||
, value_end(value_end) |
|||
, is_end(is_end) |
|||
{ } |
|||
friend class inclusive; |
|||
public: |
|||
constexpr iterator(const iterator&) noexcept = default; |
|||
constexpr T operator*(void) const noexcept |
|||
{ |
|||
return value; |
|||
} |
|||
/* can be constexpr in C++14 */ |
|||
#if __cpp_constexpr >= 201304L |
|||
constexpr |
|||
#endif |
|||
iterator& operator++(void) noexcept |
|||
{ |
|||
if (!is_end) |
|||
{ |
|||
if (value == value_end) |
|||
is_end = true; |
|||
else |
|||
++value; |
|||
} |
|||
return *this; |
|||
} |
|||
public: |
|||
constexpr bool operator==( |
|||
const iterator& other |
|||
) const noexcept |
|||
{ |
|||
return |
|||
value == other.value && |
|||
value_end == other.value_end && |
|||
is_end == other.is_end; |
|||
} |
|||
constexpr bool operator!=( |
|||
const iterator& other |
|||
) const noexcept |
|||
{ |
|||
return !(*this == other); |
|||
} |
|||
}; |
|||
public: |
|||
constexpr iterator begin(void) const noexcept |
|||
{ |
|||
return iterator(v_begin, v_end, false); |
|||
} |
|||
constexpr iterator end(void) const noexcept |
|||
{ |
|||
return iterator(v_end, v_end, true); |
|||
} |
|||
public: |
|||
constexpr inclusive(T vbegin, T vend) noexcept |
|||
: v_begin(vbegin), v_end(vend) { } |
|||
constexpr inclusive(void) noexcept |
|||
: v_begin(std::numeric_limits<T>::min()) |
|||
, v_end (std::numeric_limits<T>::max()) { } |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,371 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
safe_int.hpp |
|||
Safe integer handling utilities |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_SAFE_INT_HPP |
|||
#define FFUZZYPP_UTILS_SAFE_INT_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
#include <limits> |
|||
#include <type_traits> |
|||
|
|||
namespace ffuzzy { |
|||
namespace safe_int { |
|||
|
|||
|
|||
namespace contracts |
|||
{ |
|||
template <typename T> |
|||
static inline constexpr bool is_integral_type(void) noexcept |
|||
{ |
|||
return std::is_integral<T>::value; |
|||
} |
|||
template <typename T> |
|||
static inline constexpr bool is_signed_integral_type(void) noexcept |
|||
{ |
|||
return is_integral_type<T>() && |
|||
std::is_unsigned<T>::value == false && |
|||
std::numeric_limits<T>::max() > 0 && |
|||
std::numeric_limits<T>::min() < 0; |
|||
} |
|||
template <typename T> |
|||
static inline constexpr bool is_unsigned_integral_type(void) noexcept |
|||
{ |
|||
return is_integral_type<T>() && |
|||
std::is_unsigned<T>::value == true && |
|||
std::numeric_limits<T>::min() == 0; |
|||
} |
|||
template <typename T> |
|||
static inline constexpr bool is_binary_integral_type(void) noexcept |
|||
{ |
|||
return |
|||
std::numeric_limits<T>::radix == 2 && |
|||
std::numeric_limits<T>::digits >= 0; |
|||
} |
|||
|
|||
// Same implementation but copying for better diagnostics
|
|||
template <typename T> |
|||
struct integral_type |
|||
{ |
|||
static_assert(std::is_integral<T>::value, |
|||
"T must be an integral type."); |
|||
}; |
|||
template <typename T> |
|||
struct signed_integral_type |
|||
: integral_type<T> |
|||
{ |
|||
static_assert(std::is_unsigned<T>::value == false, |
|||
"T must be a signed integral type."); |
|||
static_assert(std::numeric_limits<T>::max() > 0, |
|||
"numeric_limits<T>::max() must be positive for signed integral type."); |
|||
static_assert(std::numeric_limits<T>::min() < 0, |
|||
"numeric_limits<T>::min() must be negative for signed integral type."); |
|||
}; |
|||
template <typename T> |
|||
struct unsigned_integral_type |
|||
: integral_type<T> |
|||
{ |
|||
static_assert(std::is_unsigned<T>::value == true, |
|||
"T must be an unsigned integral type."); |
|||
static_assert(std::numeric_limits<T>::min() == 0, |
|||
"numeric_limits<T>::min() must be zero for unsigned integral type."); |
|||
}; |
|||
template <typename T> |
|||
struct binary_integral_type |
|||
: integral_type<T> |
|||
{ |
|||
static_assert(std::numeric_limits<T>::radix == 2, |
|||
"numeric_limits<T>::radix must be 2 for binary integral type."); |
|||
static_assert(std::numeric_limits<T>::digits >= 0, |
|||
"numeric_limits<T>::digits must not be negative for binary integral type."); |
|||
}; |
|||
} |
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
template <typename T1, typename T2> |
|||
struct common_unsigned_integral_type |
|||
: contracts::unsigned_integral_type<T1> |
|||
, contracts::unsigned_integral_type<T2> |
|||
{ |
|||
typedef typename std::conditional< |
|||
std::numeric_limits<T1>::max() < std::numeric_limits<T2>::max(), |
|||
T2, T1 |
|||
>::type type; |
|||
}; |
|||
template <typename T> |
|||
struct common_unsigned_integral_type<T, T> |
|||
{ |
|||
typedef T type; |
|||
}; |
|||
|
|||
template <typename Tcommon, typename Tvalue, Tcommon Value, bool IsValid = true> |
|||
struct value_base |
|||
{ |
|||
typedef Tcommon common_type; |
|||
typedef Tvalue value_type; |
|||
static constexpr const bool is_valid = IsValid |
|||
&& std::numeric_limits<Tvalue>::min() <= Value |
|||
&& std::numeric_limits<Tvalue>::max() >= Value; |
|||
static constexpr const Tcommon value_in_common = Value; |
|||
static constexpr const Tvalue value = |
|||
is_valid ? static_cast<Tvalue>(Value) : 0; |
|||
}; |
|||
} |
|||
|
|||
|
|||
template <typename T, uintmax_t Value, bool IsValid = true> |
|||
struct uvalue |
|||
: contracts::unsigned_integral_type<T> |
|||
, public internal::value_base<uintmax_t, T, Value, IsValid> |
|||
{}; |
|||
|
|||
template <typename T, intmax_t Value, bool IsValid = true> |
|||
struct svalue |
|||
: contracts::signed_integral_type<T> |
|||
, public internal::value_base<intmax_t, T, Value, IsValid> |
|||
{}; |
|||
|
|||
|
|||
|
|||
|
|||
namespace operations |
|||
{ |
|||
|
|||
template <typename TV1, typename TV2> struct safe_add; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_add<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V1 + V2, B1 && B2 && ( |
|||
std::numeric_limits<common_type>::max() - V1 >= V2 |
|||
)> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TV1, typename TV2> struct safe_sub; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_sub<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V1 - V2, B1 && B2 && V1 >= V2> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TV1, typename TV2> struct safe_mul; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_mul<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V1 * V2, B1 && B2 && ( |
|||
V1 == 0 || V2 == 0 || |
|||
std::numeric_limits<common_type>::max() / V2 > V1 |
|||
)> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TV1, typename TV2> struct safe_div; |
|||
template <typename T1, uintmax_t V1, bool B1, typename T2, uintmax_t V2, bool B2> |
|||
struct safe_div<uvalue<T1, V1, B1>, uvalue<T2, V2, B2>> |
|||
{ |
|||
private: |
|||
typedef typename internal::common_unsigned_integral_type<T1, T2>::type common_type; |
|||
public: |
|||
typedef uvalue<common_type, V2 ? V1 / V2 : 0, B1 && B2 && V2 != 0> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TVbase, typename TVshift> struct safe_lshift; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_lshift<uvalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
// Refer C++11 specification [3.9.1 p3] for reason that
|
|||
// conversion to "unsigned" for non-negative "int" numbers is safe.
|
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits) |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_lshift<uvalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_lshift<svalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits) |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_lshift<svalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits |
|||
&& (std::numeric_limits<TB>::max() >> VS) >= VB; |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB << VS) : 0, is_valid> type; |
|||
}; |
|||
|
|||
|
|||
template <typename TVbase, typename TVshift> struct safe_rshift; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_rshift<uvalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits); |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, uintmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_rshift<uvalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits; |
|||
public: |
|||
typedef uvalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, uintmax_t VS, bool BS> |
|||
struct safe_rshift<svalue<TB, VB, BB>, uvalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS < static_cast<unsigned>(std::numeric_limits<TB>::digits); |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
template <typename TB, intmax_t VB, bool BB, typename TS, intmax_t VS, bool BS> |
|||
struct safe_rshift<svalue<TB, VB, BB>, svalue<TS, VS, BS>> |
|||
: contracts::binary_integral_type<TB> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = BB && BS |
|||
&& VS >= 0 |
|||
&& VS < std::numeric_limits<TB>::digits; |
|||
public: |
|||
typedef svalue<TB, is_valid ? (VB >> VS) : 0, is_valid> type; |
|||
}; |
|||
|
|||
|
|||
namespace internal |
|||
{ |
|||
// internal::value_base performs some required tests
|
|||
template <typename Tnew, typename TV> struct safe_cast_to_u; |
|||
template <typename Tnew, typename Told, uintmax_t V, bool B> |
|||
struct safe_cast_to_u<Tnew, uvalue<Told, V, B>> |
|||
{ |
|||
typedef uvalue<Tnew, V, B> type; |
|||
}; |
|||
template <typename Tnew, typename Told, intmax_t V, bool B> |
|||
struct safe_cast_to_u<Tnew, svalue<Told, V, B>> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = B && V >= 0; |
|||
public: |
|||
typedef uvalue<Tnew, |
|||
is_valid ? static_cast<uintmax_t>(V) : 0, |
|||
is_valid> type; |
|||
}; |
|||
template <typename Tnew, typename TV> struct safe_cast_to_s; |
|||
template <typename Tnew, typename Told, intmax_t V, bool B> |
|||
struct safe_cast_to_s<Tnew, svalue<Told, V, B>> |
|||
{ |
|||
typedef svalue<Tnew, V, B> type; |
|||
}; |
|||
template <typename Tnew, typename Told, uintmax_t V, bool B> |
|||
struct safe_cast_to_s<Tnew, uvalue<Told, V, B>> |
|||
{ |
|||
private: |
|||
static constexpr const bool is_valid = B |
|||
&& V <= static_cast<uintmax_t>(std::numeric_limits<intmax_t>::max()); |
|||
public: |
|||
typedef svalue<Tnew, |
|||
is_valid ? static_cast<intmax_t>(V) : 0, |
|||
is_valid> type; |
|||
}; |
|||
} |
|||
template <typename Tnew, typename TV> |
|||
struct safe_cast |
|||
: public std::conditional< |
|||
contracts::is_unsigned_integral_type<Tnew>(), |
|||
internal::safe_cast_to_u<Tnew, TV>, |
|||
typename std::conditional< |
|||
contracts::is_signed_integral_type<Tnew>(), |
|||
internal::safe_cast_to_s<Tnew, TV>, void |
|||
>::type |
|||
>::type {}; |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
template <typename TV1, typename TV2> |
|||
using safe_add = typename operations::safe_add<TV1, TV2>::type; |
|||
template <typename TV1, typename TV2> |
|||
using safe_sub = typename operations::safe_sub<TV1, TV2>::type; |
|||
template <typename TV1, typename TV2> |
|||
using safe_mul = typename operations::safe_mul<TV1, TV2>::type; |
|||
template <typename TV1, typename TV2> |
|||
using safe_div = typename operations::safe_div<TV1, TV2>::type; |
|||
template <typename TVbase, typename TVshift> |
|||
using safe_lshift = typename operations::safe_lshift<TVbase, TVshift>::type; |
|||
template <typename TVbase, typename TVshift> |
|||
using safe_rshift = typename operations::safe_rshift<TVbase, TVshift>::type; |
|||
template <typename Tnew, typename TV> |
|||
using safe_cast = typename operations::safe_cast<Tnew, TV>::type; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,97 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
static_assert_query.hpp |
|||
Basic queries for static assertions |
|||
|
|||
Copyright (C) 2014 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_STATIC_ASSERT_QUERY_HPP |
|||
#define FFUZZYPP_UTILS_STATIC_ASSERT_QUERY_HPP |
|||
|
|||
#include <cstdint> |
|||
|
|||
namespace ffuzzy { |
|||
namespace static_assert_query { |
|||
|
|||
namespace internal |
|||
{ |
|||
template <template <uintmax_t> class T, uintmax_t a, uintmax_t b> |
|||
struct is_all_bounds |
|||
{ |
|||
static_assert(a <= b, "bounds must be valid (a must not exceed b)."); |
|||
/*
|
|||
For unsigned types, |
|||
a/2 + b/2 + ((a%2)+(b%2))/2 |
|||
is a remplacement of |
|||
(a+b)/2 |
|||
while preventing arithmetic overflow of (a+b). |
|||
*/ |
|||
static constexpr const bool value = |
|||
a > b ? false : |
|||
is_all_bounds<T, a, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2>::value && |
|||
is_all_bounds<T, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2 + 1, b>::value; |
|||
}; |
|||
template <template <uintmax_t> class T, uintmax_t a> |
|||
struct is_all_bounds<T, a, a> |
|||
{ |
|||
static constexpr const bool value = T<a>::value; |
|||
}; |
|||
|
|||
template <template <uintmax_t> class T, uintmax_t a, uintmax_t b> |
|||
struct is_any_bounds |
|||
{ |
|||
static_assert(a <= b, "bounds must be valid (a must not exceed b)."); |
|||
static constexpr const bool value = |
|||
a > b ? false : |
|||
is_any_bounds<T, a, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2>::value || |
|||
is_any_bounds<T, a / 2 + b / 2 + ((a % 2) + (b % 2)) / 2 + 1, b>::value; |
|||
}; |
|||
template <template <uintmax_t> class T, uintmax_t a> |
|||
struct is_any_bounds<T, a, a> |
|||
{ |
|||
static constexpr const bool value = T<a>::value; |
|||
}; |
|||
} |
|||
|
|||
|
|||
template <template <uintmax_t> class T, uintmax_t n> |
|||
struct is_all |
|||
{ |
|||
static constexpr const bool value = internal::is_all_bounds<T, 0, n-1>::value; |
|||
}; |
|||
template <template <uintmax_t> class T> |
|||
struct is_all<T, 0> |
|||
{ |
|||
static constexpr const bool value = true; |
|||
}; |
|||
|
|||
template <template <uintmax_t> class T, uintmax_t n> |
|||
struct is_any |
|||
{ |
|||
static constexpr const bool value = internal::is_any_bounds<T, 0, n-1>::value; |
|||
}; |
|||
template <template <uintmax_t> class T> |
|||
struct is_any<T, 0> |
|||
{ |
|||
static constexpr const bool value = false; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,67 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ Helper Libraries |
|||
|
|||
type_modifier.hpp |
|||
C++11 type modifier utilities |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_UTILS_TYPE_MODIFIER_HPP |
|||
#define FFUZZYPP_UTILS_TYPE_MODIFIER_HPP |
|||
|
|||
#include <type_traits> |
|||
|
|||
namespace ffuzzy { |
|||
namespace type_mod { |
|||
|
|||
template <typename T, bool IsConst, bool IsVolatile> |
|||
struct cv_selector; |
|||
template <typename T> |
|||
struct cv_selector<T, false, false> |
|||
{ |
|||
typedef T type; |
|||
}; |
|||
template <typename T> |
|||
struct cv_selector<T, true, false> |
|||
{ |
|||
typedef const T type; |
|||
}; |
|||
template <typename T> |
|||
struct cv_selector<T, false, true> |
|||
{ |
|||
typedef volatile T type; |
|||
}; |
|||
template <typename T> |
|||
struct cv_selector<T, true, true> |
|||
{ |
|||
typedef const volatile T type; |
|||
}; |
|||
template < |
|||
typename T, |
|||
typename Tmatch, |
|||
bool IsConst = std::is_const<T>::value, |
|||
bool IsVolatile = std::is_volatile<T>::value |
|||
> |
|||
struct cv_match |
|||
{ |
|||
typedef typename cv_selector<Tmatch, IsConst, IsVolatile>::type type; |
|||
}; |
|||
|
|||
}} |
|||
|
|||
#endif |
@ -0,0 +1,7 @@ |
|||
* |
|||
!.gitignore |
|||
!Makefile.am |
|||
!/*.cpp |
|||
!/cases/ |
|||
!/cases/**/ |
|||
!/cases/**/*.hpp |
@ -0,0 +1,76 @@ |
|||
#
|
|||
#
|
|||
# ffuzzy++ : C++ implementation of fast fuzzy hashing
|
|||
#
|
|||
# tests/Makefile.am
|
|||
# Makefile template for tests
|
|||
#
|
|||
# Copyright (C) 2014 Tsukasa OI.
|
|||
#
|
|||
#
|
|||
# Permission to use, copy, modify, and/or distribute this software for
|
|||
# any purpose with or without fee is hereby granted, provided that the
|
|||
# above copyright notice and this permission notice appear in all copies.
|
|||
#
|
|||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#
|
|||
#
|
|||
AM_CPPFLAGS = -I$(top_srcdir) |
|||
LIBS = -lgtest -lgtest_main |
|||
|
|||
if ENABLE_TESTS |
|||
noinst_PROGRAMS = test-precond test-small |
|||
test_precond_SOURCES = test_precond.cpp |
|||
test_small_SOURCES = test_small.cpp |
|||
if ENABLE_COMPATIBILITY_TESTS |
|||
noinst_PROGRAMS += test-compatibility-small test-compatibility-large |
|||
test_compatibility_small_SOURCES = test_compatibility_small.cpp |
|||
test_compatibility_large_SOURCES = test_compatibility_large.cpp |
|||
endif |
|||
endif |
|||
|
|||
EXTRA_DIST = \
|
|||
.gitignore \
|
|||
cases/precond/digest.hpp \
|
|||
cases/precond/digest_filesize.hpp \
|
|||
cases/precond/digest_position_array_base.hpp \
|
|||
cases/precond/position_array.hpp \
|
|||
cases/precond/utils/minmax.hpp \
|
|||
cases/precond/utils/static_assert_query.hpp \
|
|||
cases/precond/utils/type_modifier.hpp \
|
|||
cases/compatibility/common/blockhash_comparison_min_matching.hpp \
|
|||
cases/compatibility/large/blockhash_comparison_min_matching.hpp \
|
|||
cases/compatibility/small/blockhash_comparison_max_matching.hpp \
|
|||
cases/compatibility/small/blockhash_comparison_min_matching.hpp \
|
|||
cases/compatibility/small/digest_comparison_identical.hpp \
|
|||
cases/compatibility/small/digest_comparison_identical_2_9_1.hpp \
|
|||
cases/compatibility/small/digest_comparison_identical_2_9_2.hpp \
|
|||
cases/compatibility/small/digest_data_naturality.hpp \
|
|||
cases/compatibility/small/digest_generator_forms.hpp \
|
|||
cases/compatibility/small/digest_generator_initial_guess.hpp \
|
|||
cases/compatibility/small/digest_generator_nil.hpp \
|
|||
cases/compatibility/small/digest_generator_rolling_hash_overflow.hpp \
|
|||
cases/compatibility/small/digest_normalization.hpp \
|
|||
cases/compatibility/small/digest_position_array_usage.hpp \
|
|||
cases/compatibility/small/digest_usage.hpp \
|
|||
cases/small/base64.hpp \
|
|||
cases/small/common_substr.hpp \
|
|||
cases/small/context_hash.hpp \
|
|||
cases/small/digest_blocksize.hpp \
|
|||
cases/small/digest_comparison_score_cap.hpp \
|
|||
cases/small/digest_generator.hpp \
|
|||
cases/small/edit_dist.hpp \
|
|||
cases/small/nosequences.hpp \
|
|||
cases/small/position_array.hpp \
|
|||
cases/small/rolling_hash.hpp \
|
|||
cases/small/sequences.hpp \
|
|||
cases/small/terminators.hpp \
|
|||
cases/small/transform.hpp \
|
|||
cases/small/utils/minmax.hpp \
|
|||
cases/small/utils/ranges.hpp |
@ -0,0 +1,98 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/common/blockhash_comparison_min_matching.hpp |
|||
Tests for minimum matching scores (common parts) |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_COMMON_BLOCKHASH_COMPARISON_MIN_MATCHING_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_COMMON_BLOCKHASH_COMPARISON_MIN_MATCHING_HPP |
|||
|
|||
#include <cassert> |
|||
#include <string> |
|||
|
|||
|
|||
class BlockhashComparisonMinMatchingTests : public ::testing::Test |
|||
{ |
|||
protected: |
|||
const string s1template; |
|||
const string s2template; |
|||
const string filler; |
|||
private: |
|||
static string construct_template( |
|||
string::size_type len, |
|||
const char c1, |
|||
const char c2 |
|||
) |
|||
{ |
|||
string str; |
|||
for (string::size_type i = 0; i < len; i++) |
|||
str.append(1, (i & 1) ? c2 : c1); |
|||
return str; |
|||
} |
|||
public: |
|||
BlockhashComparisonMinMatchingTests(void) |
|||
: ::testing::Test() |
|||
, s1template(construct_template(digest_params::max_blockhash_len, '0', '1')) |
|||
, s2template(construct_template(digest_params::max_blockhash_len, '2', '3')) |
|||
, filler("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") |
|||
{ |
|||
} |
|||
private: |
|||
static string construct_from_template( |
|||
const string& template_str, |
|||
const string& filler_str, |
|||
const string::size_type total_length, |
|||
const string::size_type filler_length, |
|||
const string::size_type filler_index |
|||
) |
|||
{ |
|||
assert(template_str.size() != 0); |
|||
assert(template_str.size() >= total_length); |
|||
assert(filler_str.size() != 0); |
|||
assert(filler_length + filler_index <= total_length); |
|||
string str = template_str.substr(0, total_length); |
|||
for (string::size_type i = 0; i < filler_length; i++) |
|||
str[filler_index + i] = filler_str[i % filler_str.size()]; |
|||
return str; |
|||
} |
|||
protected: |
|||
string MinMatchingTemplate1( |
|||
blockhash_len_t len, |
|||
blockhash_len_t index |
|||
) |
|||
{ |
|||
return construct_from_template( |
|||
s1template, filler, |
|||
len, blockhash_comparison_params::min_match_len, index |
|||
); |
|||
} |
|||
string MinMatchingTemplate2( |
|||
blockhash_len_t len, |
|||
blockhash_len_t index |
|||
) |
|||
{ |
|||
return construct_from_template( |
|||
s2template, filler, |
|||
len, blockhash_comparison_params::min_match_len, index |
|||
); |
|||
} |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,70 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/large/blockhash_comparison_min_matching.hpp |
|||
Tests for minimum matching scores (large) |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_LARGE_BLOCKHASH_COMPARISON_MIN_MATCHING_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_LARGE_BLOCKHASH_COMPARISON_MIN_MATCHING_HPP |
|||
|
|||
#include <cassert> |
|||
#include <algorithm> |
|||
#include <string> |
|||
|
|||
#include "../common/blockhash_comparison_min_matching.hpp" |
|||
|
|||
|
|||
class BlockhashComparisonMinMatchingTestsWithBlocksizeIndex |
|||
: public BlockhashComparisonMinMatchingTests |
|||
, public ::testing::WithParamInterface<unsigned> |
|||
{ |
|||
}; |
|||
|
|||
TEST_P(BlockhashComparisonMinMatchingTestsWithBlocksizeIndex, MinMatchingScore) |
|||
{ |
|||
for (blockhash_len_t s1len = blockhash_comparison_params::min_match_len; |
|||
s1len <= digest_params::max_blockhash_len; s1len++) |
|||
{ |
|||
for (blockhash_len_t s1idx = 0; s1idx <= s1len - blockhash_comparison_params::min_match_len; s1idx++) |
|||
{ |
|||
string s1 = MinMatchingTemplate1(s1len, s1idx); |
|||
for (blockhash_len_t s2len = blockhash_comparison_params::min_match_len; |
|||
s2len <= digest_params::max_blockhash_len; s2len++) |
|||
{ |
|||
for (blockhash_len_t s2idx = 0; s2idx <= s2len - blockhash_comparison_params::min_match_len; s2idx++) |
|||
{ |
|||
string s2 = MinMatchingTemplate2(s2len, s2idx); |
|||
unsigned bi = GetParam(); |
|||
digest_blocksize_t bs = digest_blocksize::at(bi); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::min_matching_score(bs, s1len, s2len), |
|||
blockhash_comparison<>::score(s1.data(), s1len, s2.data(), s2len, bs) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
INSTANTIATE_TEST_CASE_P(BlocksizeCases, BlockhashComparisonMinMatchingTestsWithBlocksizeIndex, |
|||
::testing::Range<unsigned>(0u, digest_blocksize::number_of_blockhashes)); |
|||
|
|||
|
|||
#endif |
@ -0,0 +1,183 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/blockhash_comparison_max_matching.hpp |
|||
Tests for maximum matching scores |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_BLOCKHASH_COMPARISON_MAX_MATCHING_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_BLOCKHASH_COMPARISON_MAX_MATCHING_HPP |
|||
|
|||
#include <cassert> |
|||
#include <string> |
|||
|
|||
|
|||
class BlockhashComparisonMaxMatchingTests : public ::testing::Test |
|||
{ |
|||
protected: |
|||
const string template_str; |
|||
public: |
|||
BlockhashComparisonMaxMatchingTests(void) |
|||
: ::testing::Test() |
|||
, template_str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") |
|||
{ |
|||
} |
|||
protected: |
|||
string MaxMatchingTemplate(blockhash_len_t len) |
|||
{ |
|||
return template_str.substr(0, len); |
|||
} |
|||
string MaxMatchingTemplateSubstring( |
|||
blockhash_len_t len_max, |
|||
blockhash_len_t len_min, |
|||
blockhash_len_t index |
|||
) |
|||
{ |
|||
assert(len_max > len_min); |
|||
assert(index <= len_max - len_min); |
|||
return template_str.substr(index, len_min); |
|||
} |
|||
}; |
|||
|
|||
|
|||
TEST_F(BlockhashComparisonMaxMatchingTests, TemplateSanity) |
|||
{ |
|||
EXPECT_EQ("A", MaxMatchingTemplate(1)); |
|||
EXPECT_EQ("AB", MaxMatchingTemplate(2)); |
|||
EXPECT_EQ("ABC", MaxMatchingTemplate(3)); |
|||
EXPECT_EQ("ABCD", MaxMatchingTemplate(4)); |
|||
EXPECT_EQ("ABCDE", MaxMatchingTemplate(5)); |
|||
EXPECT_EQ("ABCDEF", MaxMatchingTemplate(6)); |
|||
EXPECT_EQ("ABCDEFG", MaxMatchingTemplate(7)); |
|||
EXPECT_EQ("ABCDEFGH", MaxMatchingTemplate(8)); |
|||
EXPECT_EQ("ABCDEFGHI", MaxMatchingTemplate(9)); |
|||
EXPECT_EQ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", MaxMatchingTemplate(64)); |
|||
EXPECT_EQ("AB", MaxMatchingTemplateSubstring(3, 2, 0)); |
|||
EXPECT_EQ("BC", MaxMatchingTemplateSubstring(3, 2, 1)); |
|||
EXPECT_EQ("ABC", MaxMatchingTemplateSubstring(7, 3, 0)); |
|||
EXPECT_EQ("BCD", MaxMatchingTemplateSubstring(7, 3, 1)); |
|||
EXPECT_EQ("CDE", MaxMatchingTemplateSubstring(7, 3, 2)); |
|||
EXPECT_EQ("DEF", MaxMatchingTemplateSubstring(7, 3, 3)); |
|||
EXPECT_EQ("EFG", MaxMatchingTemplateSubstring(7, 3, 4)); |
|||
} |
|||
|
|||
|
|||
TEST_F(BlockhashComparisonMaxMatchingTests, CommutativeProperty) |
|||
{ |
|||
for (blockhash_len_t s1len = blockhash_comparison_params::min_match_len; |
|||
s1len <= digest_params::max_blockhash_len; s1len++) |
|||
{ |
|||
for (blockhash_len_t s2len = blockhash_comparison_params::min_match_len; |
|||
s2len <= digest_params::max_blockhash_len; s2len++) |
|||
{ |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_max_matching_score(s1len, s2len), |
|||
blockhash_comparison<>::uncapped_max_matching_score(s2len, s1len) |
|||
); |
|||
for (unsigned bi = 0; bi < digest_blocksize::number_of_blockhashes; bi++) |
|||
{ |
|||
digest_blocksize_t bs = digest_blocksize::at(bi); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::capped_max_matching_score(bs, s1len, s2len), |
|||
blockhash_comparison<>::capped_max_matching_score(bs, s2len, s1len) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::max_matching_score(bs, s1len, s2len), |
|||
blockhash_comparison<>::max_matching_score(bs, s2len, s1len) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
TEST_F(BlockhashComparisonMaxMatchingTests, MaxMatchingScoreNotEq) |
|||
{ |
|||
for (blockhash_len_t s1len = blockhash_comparison_params::min_match_len; |
|||
s1len <= digest_params::max_blockhash_len; s1len++) |
|||
{ |
|||
string s1 = MaxMatchingTemplate(s1len); |
|||
for (blockhash_len_t s2len = blockhash_comparison_params::min_match_len; s2len < s1len; s2len++) |
|||
{ |
|||
ASSERT_NE(s1len, s2len); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_max_matching_score_le(s2len, s1len), |
|||
blockhash_comparison<>::uncapped_max_matching_score(s2len, s1len) |
|||
); |
|||
for (blockhash_len_t index = 0; index <= s1len - s2len; index++) |
|||
{ |
|||
string s2 = MaxMatchingTemplateSubstring(s1len, s2len, index); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_max_matching_score_le(s2len, s1len), |
|||
blockhash_comparison<>::uncapped_score(s1.data(), s1len, s2.data(), s2len) |
|||
); |
|||
for (unsigned bi = 0; bi < digest_blocksize::number_of_blockhashes; bi++) |
|||
{ |
|||
digest_blocksize_t bs = digest_blocksize::at(bi); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::capped_max_matching_score_le(bs, s2len, s1len), |
|||
blockhash_comparison<>::capped_max_matching_score(bs, s2len, s1len) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::max_matching_score_le(bs, s2len, s1len), |
|||
blockhash_comparison<>::max_matching_score(bs, s2len, s1len) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::max_matching_score_le(bs, s2len, s1len), |
|||
blockhash_comparison<>::score(s1.data(), s1len, s2.data(), s2len, bs) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::max_matching_score_le(bs, s2len, s1len), |
|||
blockhash_comparison<>::capped_max_matching_score_le(bs, s2len, s1len) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
TEST_F(BlockhashComparisonMaxMatchingTests, MaxMatchingScoreEq) |
|||
{ |
|||
for (blockhash_len_t slen = blockhash_comparison_params::min_match_len; |
|||
slen <= digest_params::max_blockhash_len; slen++) |
|||
{ |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_max_matching_score_le(slen, slen), |
|||
blockhash_comparison<>::uncapped_max_matching_score(slen, slen) |
|||
); |
|||
string s = MaxMatchingTemplate(slen); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_max_matching_score_le(slen, slen), |
|||
blockhash_comparison<>::uncapped_score(s.data(), slen, s.data(), slen) |
|||
); |
|||
for (unsigned bi = 0; bi < digest_blocksize::number_of_blockhashes; bi++) |
|||
{ |
|||
digest_blocksize_t bs = digest_blocksize::at(bi); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::capped_max_matching_score_le(bs, slen, slen), |
|||
blockhash_comparison<>::score(s.data(), slen, s.data(), slen, bs) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::max_matching_score_le(bs, slen, slen), |
|||
blockhash_comparison<>::score_identical(slen, bs) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,144 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/blockhash_comparison_min_matching.hpp |
|||
Tests for minimum matching scores (small) |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_BLOCKHASH_COMPARISON_MIN_MATCHING_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_BLOCKHASH_COMPARISON_MIN_MATCHING_HPP |
|||
|
|||
#include <cassert> |
|||
#include <algorithm> |
|||
#include <string> |
|||
|
|||
#include "../common/blockhash_comparison_min_matching.hpp" |
|||
|
|||
|
|||
TEST_F(BlockhashComparisonMinMatchingTests, TemplateSanity) |
|||
{ |
|||
if (s1template.size() >= 2) |
|||
{ |
|||
EXPECT_EQ("01", s1template.substr(0, 2)); |
|||
EXPECT_EQ("23", s2template.substr(0, 2)); |
|||
} |
|||
if (s1template.size() >= 8) |
|||
{ |
|||
EXPECT_EQ("01010101", s1template.substr(0, 8)); |
|||
EXPECT_EQ("23232323", s2template.substr(0, 8)); |
|||
} |
|||
|
|||
EXPECT_EQ("ABCDEFG", MinMatchingTemplate1(7, 0)); |
|||
EXPECT_EQ("ABCDEFG", MinMatchingTemplate2(7, 0)); |
|||
|
|||
EXPECT_EQ("ABCDEFG1010101010101", MinMatchingTemplate1(20, 0)); |
|||
EXPECT_EQ("0ABCDEFG010101010101", MinMatchingTemplate1(20, 1)); |
|||
EXPECT_EQ("01ABCDEFG10101010101", MinMatchingTemplate1(20, 2)); |
|||
EXPECT_EQ("010ABCDEFG0101010101", MinMatchingTemplate1(20, 3)); |
|||
EXPECT_EQ("0101ABCDEFG101010101", MinMatchingTemplate1(20, 4)); |
|||
EXPECT_EQ("01010ABCDEFG01010101", MinMatchingTemplate1(20, 5)); |
|||
EXPECT_EQ("010101ABCDEFG1010101", MinMatchingTemplate1(20, 6)); |
|||
EXPECT_EQ("0101010ABCDEFG010101", MinMatchingTemplate1(20, 7)); |
|||
EXPECT_EQ("01010101ABCDEFG10101", MinMatchingTemplate1(20, 8)); |
|||
EXPECT_EQ("010101010ABCDEFG0101", MinMatchingTemplate1(20, 9)); |
|||
EXPECT_EQ("0101010101ABCDEFG101", MinMatchingTemplate1(20, 10)); |
|||
EXPECT_EQ("01010101010ABCDEFG01", MinMatchingTemplate1(20, 11)); |
|||
EXPECT_EQ("010101010101ABCDEFG1", MinMatchingTemplate1(20, 12)); |
|||
EXPECT_EQ("0101010101010ABCDEFG", MinMatchingTemplate1(20, 13)); |
|||
|
|||
EXPECT_EQ("ABCDEFG3232323232323", MinMatchingTemplate2(20, 0)); |
|||
EXPECT_EQ("2ABCDEFG232323232323", MinMatchingTemplate2(20, 1)); |
|||
EXPECT_EQ("23ABCDEFG32323232323", MinMatchingTemplate2(20, 2)); |
|||
EXPECT_EQ("232ABCDEFG2323232323", MinMatchingTemplate2(20, 3)); |
|||
EXPECT_EQ("2323ABCDEFG323232323", MinMatchingTemplate2(20, 4)); |
|||
EXPECT_EQ("23232ABCDEFG23232323", MinMatchingTemplate2(20, 5)); |
|||
EXPECT_EQ("232323ABCDEFG3232323", MinMatchingTemplate2(20, 6)); |
|||
EXPECT_EQ("2323232ABCDEFG232323", MinMatchingTemplate2(20, 7)); |
|||
EXPECT_EQ("23232323ABCDEFG32323", MinMatchingTemplate2(20, 8)); |
|||
EXPECT_EQ("232323232ABCDEFG2323", MinMatchingTemplate2(20, 9)); |
|||
EXPECT_EQ("2323232323ABCDEFG323", MinMatchingTemplate2(20, 10)); |
|||
EXPECT_EQ("23232323232ABCDEFG23", MinMatchingTemplate2(20, 11)); |
|||
EXPECT_EQ("232323232323ABCDEFG3", MinMatchingTemplate2(20, 12)); |
|||
EXPECT_EQ("2323232323232ABCDEFG", MinMatchingTemplate2(20, 13)); |
|||
|
|||
EXPECT_EQ("ABCDEFG101010101010101010101010101010101010101010101010101010101", MinMatchingTemplate1(64, 0)); |
|||
EXPECT_EQ("010101010101010101010101010101010101010101010101010101010ABCDEFG", MinMatchingTemplate1(64, 57)); |
|||
EXPECT_EQ("ABCDEFG323232323232323232323232323232323232323232323232323232323", MinMatchingTemplate2(64, 0)); |
|||
EXPECT_EQ("232323232323232323232323232323232323232323232323232323232ABCDEFG", MinMatchingTemplate2(64, 57)); |
|||
} |
|||
|
|||
|
|||
TEST_F(BlockhashComparisonMinMatchingTests, MinMatchingScoreSmall) |
|||
{ |
|||
for (blockhash_len_t s1len = blockhash_comparison_params::min_match_len; |
|||
s1len <= digest_params::max_blockhash_len; s1len++) |
|||
{ |
|||
string s1 = MinMatchingTemplate1(s1len, 0); |
|||
for (blockhash_len_t s2len = blockhash_comparison_params::min_match_len; |
|||
s2len <= digest_params::max_blockhash_len; s2len++) |
|||
{ |
|||
string s2 = MinMatchingTemplate2(s2len, s2len - blockhash_comparison_params::min_match_len); |
|||
for (unsigned bi = 0; bi < digest_blocksize::number_of_blockhashes; bi++) |
|||
{ |
|||
digest_blocksize_t bs = digest_blocksize::at(bi); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_min_matching_score(s1len, s2len), |
|||
blockhash_comparison<>::uncapped_score(s1.data(), s1len, s2.data(), s2len) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::min_matching_score(bs, s1len, s2len), |
|||
blockhash_comparison<>::score(s1.data(), s1len, s2.data(), s2len, bs) |
|||
); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::min_matching_score(bs, s1len, s2len), |
|||
std::min( |
|||
blockhash_comparison<>::uncapped_min_matching_score(s1len, s2len), |
|||
blockhash_comparison<>::score_cap(bs, s1len, s2len) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
TEST_F(BlockhashComparisonMinMatchingTests, MinMatchingScoreLargeSubset) |
|||
{ |
|||
for (blockhash_len_t s1len = blockhash_comparison_params::min_match_len; |
|||
s1len <= digest_params::max_blockhash_len; s1len++) |
|||
{ |
|||
for (blockhash_len_t s1idx = 0; s1idx <= s1len - blockhash_comparison_params::min_match_len; s1idx++) |
|||
{ |
|||
string s1 = MinMatchingTemplate1(s1len, s1idx); |
|||
for (blockhash_len_t s2len = blockhash_comparison_params::min_match_len; |
|||
s2len <= digest_params::max_blockhash_len; s2len++) |
|||
{ |
|||
for (blockhash_len_t s2idx = 0; s2idx <= s2len - blockhash_comparison_params::min_match_len; s2idx++) |
|||
{ |
|||
string s2 = MinMatchingTemplate2(s2len, s2idx); |
|||
ASSERT_EQ( |
|||
blockhash_comparison<>::uncapped_min_matching_score(s1len, s2len), |
|||
blockhash_comparison<>::uncapped_score(s1.data(), s1len, s2.data(), s2len) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,47 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_comparison_identical.hpp |
|||
Tests for identical digest comparison (compare with version 2.12) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_COMPARISON_IDENTICAL_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_COMPARISON_IDENTICAL_HPP |
|||
|
|||
#include <string> |
|||
|
|||
|
|||
// Data used in tests are retrieved partially from ssdeep and others from ssdeep 2.9, x86_64
|
|||
// (because ssdeep 2.12 currently has a critical bug regarding identical digest comparison).
|
|||
|
|||
TEST(DigestComparisonIdenticalTests, NormalizationComparisonTest) |
|||
{ |
|||
EXPECT_EQ(100, digest_comparison<>::compare("3:AAAAAAA:", "3:AAAAAAAAAAAAAA:")); |
|||
EXPECT_EQ(100, digest_comparison<>::compare("3::AAAAAAA", "3::AAAAAAAAAAAAAA")); |
|||
EXPECT_EQ(100, digest_comparison<>::compare("3:AAAAAABCCCCCC:", "3:AAABCCC:")); |
|||
EXPECT_EQ( 7, digest_comparison<>::compare("3:AAAAAABCCCCCC:D", "3:AAABCCC:E")); |
|||
EXPECT_EQ(100, digest_comparison<>::compare("3::AAAAAABCCCCCC", "3::AAABCCC")); |
|||
EXPECT_EQ( 14, digest_comparison<>::compare("3:D:AAAAAABCCCCCC", "3:E:AAABCCC")); |
|||
EXPECT_EQ(100, digest_comparison<>::compare( |
|||
"3:aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh01234567:", |
|||
"3:aaabbbcccdddeeefffggghhh01234567:" |
|||
)); |
|||
} |
|||
|
|||
#endif |
File diff suppressed because it is too large
@ -0,0 +1,46 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_comparison_identical_2_9_2.hpp |
|||
Tests for identical digest comparison (compare with version 2.9; part 2) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_COMPARISON_IDENTICAL_2_9_2_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_COMPARISON_IDENTICAL_2_9_2_HPP |
|||
|
|||
#include <string> |
|||
|
|||
|
|||
// Data used in tests are retrieved from ssdeep 2.9, x86_64
|
|||
|
|||
TEST_F(DigestComparisonIdenticalTests_2_9, MinimumNormalizationComparisonTest) |
|||
{ |
|||
EXPECT_EQ( 0, digest_comparison<comparison_version::v2_9>::compare("3:AAAAAAA:", "3:AAAAAAAAAAAAAA:")); |
|||
EXPECT_EQ( 0, digest_comparison<comparison_version::v2_9>::compare("3::AAAAAAA", "3::AAAAAAAAAAAAAA")); |
|||
EXPECT_EQ( 7, digest_comparison<comparison_version::v2_9>::compare("3:AAAAAABCCCCCC:", "3:AAABCCC:")); |
|||
EXPECT_EQ( 7, digest_comparison<comparison_version::v2_9>::compare("3:AAAAAABCCCCCC:D", "3:AAABCCC:E")); |
|||
EXPECT_EQ( 14, digest_comparison<comparison_version::v2_9>::compare("3::AAAAAABCCCCCC", "3::AAABCCC")); |
|||
EXPECT_EQ( 14, digest_comparison<comparison_version::v2_9>::compare("3:D:AAAAAABCCCCCC", "3:E:AAABCCC")); |
|||
EXPECT_EQ( 32, digest_comparison<comparison_version::v2_9>::compare( |
|||
"3:aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh01234567:", |
|||
"3:aaabbbcccdddeeefffggghhh01234567:" |
|||
)); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,47 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_data_naturality.hpp |
|||
Tests for digest data naturality |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_DATA_NATURALITY_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_DATA_NATURALITY_HPP |
|||
|
|||
#include <string> |
|||
|
|||
|
|||
TEST(DigestDataNaturalityTests, NaturalCases) |
|||
{ |
|||
EXPECT_TRUE(digest_t("3::").is_natural()); |
|||
EXPECT_TRUE(digest_t("3:AA:A").is_natural()); |
|||
} |
|||
|
|||
TEST(DigestDataNaturalityTests, UnnaturalCases) |
|||
{ |
|||
// block size 4 is not natural
|
|||
EXPECT_FALSE(digest_t("4::").is_natural()); |
|||
// second block hash must not be longer than the first one
|
|||
EXPECT_FALSE(digest_t("3:A:AA").is_natural()); |
|||
// non-Base64 character may not be included.
|
|||
EXPECT_FALSE(digest_t("3:*:A").is_natural()); |
|||
EXPECT_FALSE(digest_t("3:A:*").is_natural()); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,128 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_generator_forms.hpp |
|||
Tests for digest generator (result forms) |
|||
|
|||
Copyright (C) 2017 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_FORMS_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_FORMS_HPP |
|||
|
|||
#include <string> |
|||
|
|||
#include "digest_generator_initial_guess.hpp" |
|||
|
|||
|
|||
TEST(DigestGeneratorResultFormTests, DigestReturningVariantsTest) |
|||
{ |
|||
digest_generator gen; |
|||
DigestGeneratorInitialGuessTests::fill_seq(gen, 64, 1536); |
|||
EXPECT_EQ("24" |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000U", |
|||
gen.digest_str() |
|||
); |
|||
EXPECT_EQ("24" |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000U", |
|||
gen.digest().pretty() |
|||
); |
|||
EXPECT_EQ("24:000:000U", gen.digest_normalized().pretty()); |
|||
// expect digest_long to return non-shortened form
|
|||
EXPECT_EQ("24" |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000000000000000000000000000000000000", |
|||
gen.digest_long().pretty() |
|||
); |
|||
// expect no 'U' at the tail
|
|||
EXPECT_EQ("24:000:000", gen.digest_long_normalized().pretty()); |
|||
} |
|||
|
|||
TEST(DigestGeneratorResultFormTests, CopyDigestTest) |
|||
{ |
|||
digest_generator gen; |
|||
DigestGeneratorInitialGuessTests::fill_seq(gen, 64, 1536); |
|||
digest_t dsn; |
|||
digest_unorm_t dsu; |
|||
digest_long_t dln; |
|||
digest_long_unorm_t dlu; |
|||
EXPECT_TRUE(gen.copy_digest(dsn)); |
|||
EXPECT_EQ("24:000:000U", dsn.pretty()); |
|||
EXPECT_TRUE(gen.copy_digest(dsu)); |
|||
EXPECT_EQ("24" |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000U", |
|||
dsu.pretty() |
|||
); |
|||
// expect copy_digest to return shortened form
|
|||
EXPECT_TRUE(gen.copy_digest(dln)); |
|||
EXPECT_EQ("24:000:000U", dln.pretty()); |
|||
EXPECT_TRUE(gen.copy_digest(dlu)); |
|||
EXPECT_EQ("24" |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000U", |
|||
dlu.pretty() |
|||
); |
|||
} |
|||
|
|||
TEST(DigestGeneratorResultFormTests, CopyDigestLongNormalizedTest) |
|||
{ |
|||
digest_generator gen; |
|||
DigestGeneratorInitialGuessTests::fill_seq(gen, 64, 1536); |
|||
digest_long_t dln; |
|||
// expect copy_digest_long_normalized to return non-shortened form
|
|||
EXPECT_TRUE(gen.copy_digest_long_normalized(dln)); |
|||
EXPECT_EQ("24:000:000", dln.pretty()); |
|||
} |
|||
|
|||
TEST(DigestGeneratorResultFormTests, CopyDigestLongTest) |
|||
{ |
|||
digest_generator gen; |
|||
DigestGeneratorInitialGuessTests::fill_seq(gen, 64, 1536); |
|||
digest_long_t dln; |
|||
digest_long_unorm_t dlu; |
|||
// expect copy_digest_long to return non-shortened form
|
|||
EXPECT_TRUE(gen.copy_digest_long(dln)); |
|||
EXPECT_EQ("24:000:000", dln.pretty()); |
|||
EXPECT_TRUE(gen.copy_digest_long(dlu)); |
|||
EXPECT_EQ("24" |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000000000000000000000000000000000000", |
|||
dlu.pretty() |
|||
); |
|||
} |
|||
|
|||
TEST(DigestGeneratorResultFormTests, CopyDigestNormalizedTest) |
|||
{ |
|||
digest_generator gen; |
|||
DigestGeneratorInitialGuessTests::fill_seq(gen, 64, 1536); |
|||
digest_unorm_t ds; |
|||
digest_long_unorm_t dl; |
|||
EXPECT_TRUE((gen.copy_digest_normalized<false, true>(ds))); |
|||
EXPECT_EQ("24:000:000U", ds.pretty()); |
|||
EXPECT_TRUE((gen.copy_digest_normalized<false, false>(dl))); |
|||
EXPECT_EQ("24:000:000U", dl.pretty()); |
|||
EXPECT_TRUE((gen.copy_digest_normalized<false, false, true>(dl))); |
|||
EXPECT_EQ("24:000:000U", dl.pretty()); |
|||
// expect no 'U' at the tail
|
|||
EXPECT_TRUE((gen.copy_digest_normalized<false, false, false>(dl))); |
|||
EXPECT_EQ("24:000:000", dl.pretty()); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,187 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_generator_initial_guess.hpp |
|||
Tests for digest generator (regarding initial guess of block size) |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_INITIAL_GUESS_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_INITIAL_GUESS_HPP |
|||
|
|||
#include <string> |
|||
|
|||
|
|||
class DigestGeneratorInitialGuessTests : public ::testing::Test |
|||
{ |
|||
public: |
|||
static void fill_seq( |
|||
digest_generator& generator, |
|||
unsigned initial_repetition, |
|||
size_t size_to_fill |
|||
) |
|||
{ |
|||
static const unsigned char zerobuf[4096] = {0}; |
|||
static const unsigned char seq30[] = { 0xfc, 0x5d, 0x5d, 0x5d, 0x5d, 0xeb, 0xf0 }; |
|||
for (unsigned i = 0; i < initial_repetition; i++) |
|||
generator.update(seq30, sizeof(seq30)); |
|||
if (size_to_fill < generator.total_size()) |
|||
FAIL() << "size_to_fill must be equal to or greater than initial fill size."; |
|||
digest_filesize_t remaining = size_to_fill - generator.total_size(); |
|||
while (remaining >= sizeof(zerobuf)) |
|||
{ |
|||
generator.update(zerobuf, sizeof(zerobuf)); |
|||
remaining -= sizeof(zerobuf); |
|||
} |
|||
generator.update(zerobuf, remaining); |
|||
} |
|||
}; |
|||
|
|||
|
|||
TEST_F(DigestGeneratorInitialGuessTests, BlockHashIndexGuessedByFilesizeBelowBorder) |
|||
{ |
|||
for (unsigned i = 0; i < digest_blocksize::number_of_blockhashes; i++) |
|||
{ |
|||
EXPECT_EQ(i, digest_generator::blockhash_index_guessed_by_filesize( |
|||
digest_generator::guessed_filesize_at(i) |
|||
)) << "guessed index for guessed file size at " << i << " should be same as " << i << "."; |
|||
} |
|||
} |
|||
|
|||
TEST_F(DigestGeneratorInitialGuessTests, BlockHashIndexGuessedByFilesizeAboveBorder) |
|||
{ |
|||
for (unsigned i = 0; i < digest_blocksize::number_of_blockhashes - 1; i++) |
|||
{ |
|||
EXPECT_EQ(i + 1, digest_generator::blockhash_index_guessed_by_filesize( |
|||
digest_generator::guessed_filesize_at(i) + 1 |
|||
)) << "guessed file size must be maximum file size for block size index " << i << "."; |
|||
} |
|||
} |
|||
|
|||
TEST_F(DigestGeneratorInitialGuessTests, BlockHashIndexGuessedByFilesizeBelowBorderWithStart) |
|||
{ |
|||
for (unsigned j = 0; j < digest_blocksize::number_of_blockhashes; j++) |
|||
{ |
|||
for (unsigned i = 0; i < digest_blocksize::number_of_blockhashes; i++) |
|||
{ |
|||
EXPECT_EQ(std::max(i, j), digest_generator::blockhash_index_guessed_by_filesize( |
|||
digest_generator::guessed_filesize_at(i), j)) |
|||
<< "guessed index for guessed file size at " << i |
|||
<< " and start index of" << j << " should be same as " |
|||
<< std::max(i, j) << "."; |
|||
} |
|||
} |
|||
} |
|||
|
|||
TEST_F(DigestGeneratorInitialGuessTests, BlockHashIndexGuessedByFilesizeAboveBorderWithStart) |
|||
{ |
|||
for (unsigned j = 0; j < digest_blocksize::number_of_blockhashes; j++) |
|||
{ |
|||
for (unsigned i = 0; i < digest_blocksize::number_of_blockhashes - 1; i++) |
|||
{ |
|||
EXPECT_EQ(std::max(i + 1, j), digest_generator::blockhash_index_guessed_by_filesize( |
|||
digest_generator::guessed_filesize_at(i) + 1, j)) |
|||
<< "guessed file size must be maximum size for block size index " << i |
|||
<< " and still guessed block size index must not be less than " << j << "."; |
|||
} |
|||
} |
|||
} |
|||
|
|||
TEST_F(DigestGeneratorInitialGuessTests, InitialBlocksizeGuessFalseTest) |
|||
{ |
|||
digest_generator gen; |
|||
for (unsigned index = 2; index < 18; index++) |
|||
{ |
|||
digest_filesize_t sz; |
|||
// maximum size for specific block size estimate
|
|||
sz = digest_generator::guessed_filesize_at(index); |
|||
gen.reset(); |
|||
gen.set_file_size_constant(sz); |
|||
fill_seq(gen, 31, sz); |
|||
ASSERT_EQ(sz, gen.total_size()); |
|||
EXPECT_EQ( |
|||
string("3:0000000000000000000000000000000:0000000000000000000000000000000"), |
|||
gen.digest_str() |
|||
); |
|||
// ... plus one (next block size? no, for this test)
|
|||
sz++; |
|||
gen.reset(); |
|||
gen.set_file_size_constant(sz); |
|||
fill_seq(gen, 31, sz); |
|||
ASSERT_EQ(sz, gen.total_size()); |
|||
EXPECT_EQ( |
|||
string("3:0000000000000000000000000000000:0000000000000000000000000000000"), |
|||
gen.digest_str() |
|||
); |
|||
} |
|||
} |
|||
|
|||
TEST_F(DigestGeneratorInitialGuessTests, InitialBlocksizeGuessTrueTest) |
|||
{ |
|||
digest_generator gen; |
|||
ASSERT_EQ(12, digest_blocksize::at(2)); |
|||
ASSERT_EQ(768, digest_generator::guessed_filesize_at(2)); |
|||
for (unsigned index = 2; index < 18; index++) |
|||
{ |
|||
digest_filesize_t sz; |
|||
// maximum size for specific block size estimate
|
|||
sz = digest_generator::guessed_filesize_at(index); |
|||
gen.reset(); |
|||
gen.set_file_size_constant(sz); |
|||
fill_seq(gen, 32, sz); |
|||
ASSERT_EQ(sz, gen.total_size()); |
|||
EXPECT_EQ( |
|||
to_string(static_cast<unsigned long>(digest_blocksize::at(index))) + |
|||
":00000000000000000000000000000000:00000000000000000000000000000000", |
|||
gen.digest_str() |
|||
); |
|||
gen.reset(); |
|||
gen.set_file_size_constant(sz); |
|||
fill_seq(gen, 64, sz); |
|||
ASSERT_EQ(sz, gen.total_size()); |
|||
EXPECT_EQ( |
|||
to_string(static_cast<unsigned long>(digest_blocksize::at(index))) + |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000U", |
|||
gen.digest_str() |
|||
); |
|||
// ... plus one (next block size estimate!)
|
|||
sz++; |
|||
gen.reset(); |
|||
gen.set_file_size_constant(sz); |
|||
fill_seq(gen, 32, sz); |
|||
ASSERT_EQ(sz, gen.total_size()); |
|||
EXPECT_EQ( |
|||
to_string(static_cast<unsigned long>(digest_blocksize::at(index + 1))) + |
|||
":00000000000000000000000000000000:00000000000000000000000000000000", |
|||
gen.digest_str() |
|||
); |
|||
gen.reset(); |
|||
gen.set_file_size_constant(sz); |
|||
fill_seq(gen, 64, sz); |
|||
ASSERT_EQ(sz, gen.total_size()); |
|||
EXPECT_EQ( |
|||
to_string(static_cast<unsigned long>(digest_blocksize::at(index + 1))) + |
|||
":0000000000000000000000000000000000000000000000000000000000000000" |
|||
":0000000000000000000000000000000U", |
|||
gen.digest_str() |
|||
); |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,38 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_generator_nil.hpp |
|||
Tests for digest generator (for empty file) |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_NIL_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_NIL_HPP |
|||
|
|||
#include <string> |
|||
|
|||
|
|||
TEST(DigestGeneratorTests, NilTest) |
|||
{ |
|||
digest_generator gen; |
|||
EXPECT_EQ("3::", gen.digest_str()); |
|||
gen.reset(); |
|||
EXPECT_EQ("3::", gen.digest_str()); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
|
|||
ffuzzy++ : C++ implementation of fast fuzzy hasing |
|||
|
|||
tests/cases/compatibility/small/digest_generator_rolling_hash_overflow.hpp |
|||
Tests for digest generator (regarding rolling_hash overflow) |
|||
|
|||
Copyright (C) 2015 Tsukasa OI <floss_ssdeep@irq.a4lg.com> |
|||
|
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for |
|||
any purpose with or without fee is hereby granted, provided that the |
|||
above copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
|
|||
*/ |
|||
#ifndef FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_ROLLING_HASH_OVERFLOW_HPP |
|||
#define FFUZZYPP_TESTCASES_COMPATIBILITY_SMALL_DIGEST_GENERATOR_ROLLING_HASH_OVERFLOW_HPP |
|||
|
|||
#include <string> |
|||
|
|||
|
|||
// This test case is inspired by fail case from Digest::ssdeep 0.9.3, x86_64.
|
|||
|
|||
TEST(DigestGeneratorTests, RollingHashOverflowTest) |
|||
{ |
|||
digest_generator gen; |
|||
// if no mod 2^32 on final hash addition, it makes 0x100000004 (blocksize 3)
|
|||
static const unsigned char ovf1[] = {0x78, 0xfa, 0xb8, 0xfd, 0x22, 0x00}; |
|||
// if no mod 2^32 on final hash addition, it makes 0x100000001 (blocksize 6)
|
|||
static const unsigned char ovf2[] = {0xf9, 0xdf, 0x1d, 0x5a, 0xcb, 0x00}; |
|||
// if no mod 2^32 on final hash addition and XOR-SHIFT hash, it makes a blocksize 3 match
|
|||
static const unsigned char ovf3[] = {0xb2, 0xe9, 0x10, 0x5b, 0x38, 0x00}; |
|||
// if no mod 2^32 on final hash addition and XOR-SHIFT hash, it makes a blocksize 3 match
|
|||
static const unsigned char ovf4[] = {0xff, 0x00, 0x30, 0x43, 0x7a, 0x27, 0x00}; |
|||
// 6-bytes to wipe rolling hash (7 bytes requied but I chose sequences with last byte zero)
|
|||
static const unsigned char nil6[6] = {0}; |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf1, sizeof(ovf1)); |
|||
EXPECT_EQ("3:2:2", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf1, sizeof(ovf1)); |
|||
gen.update(nil6, sizeof(nil6)); |
|||
EXPECT_EQ("3::", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf2, sizeof(ovf2)); |
|||
EXPECT_EQ("3:D:D", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf2, sizeof(ovf2)); |
|||
gen.update(nil6, sizeof(nil6)); |
|||
EXPECT_EQ("3::", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf3, sizeof(ovf3)); |
|||
EXPECT_EQ("3:3:3", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf3, sizeof(ovf3)); |
|||
gen.update(nil6, sizeof(nil6)); |
|||
EXPECT_EQ("3::", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf4, sizeof(ovf4)); |
|||
EXPECT_EQ("3:Q:Q", gen.digest_str()); |
|||
|
|||
gen.reset(); |
|||
gen.update(ovf4, sizeof(ovf4)); |
|||
gen.update(nil6, sizeof(nil6)); |
|||
EXPECT_EQ("3::", gen.digest_str()); |
|||
} |
|||
|
|||
#endif |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue