huge.cpp
huge.h
main.cpp
+ sqldb.cpp
+ sqldb.h
+ sqldb_bind.h
thread_barrier.h
timer.h
)
extern unsigned editDistance;
extern bool printCompressSize;
+struct SqlFailure {
+ explicit SqlFailure(const std::string &s) : message(s) {}
+ std::string message;
+};
+
#endif // COMMON_H
#include "expressions.h"
#include "heapstats.h"
#include "huge.h"
+#include "sqldb.h"
#include "timer.h"
#include "database.h"
return 0;
}
-EngineHyperscan::EngineHyperscan(hs_database_t *db_in) : db(db_in) {
+EngineHyperscan::EngineHyperscan(hs_database_t *db_in, CompileStats cs)
+ : db(db_in), compile_stats(std::move(cs)) {
assert(db);
}
}
}
+void EngineHyperscan::printStats() const {
+ // Output summary information.
+ if (!compile_stats.sigs_name.empty()) {
+ printf("Signature set: %s\n", compile_stats.sigs_name.c_str());
+ }
+ printf("Signatures: %s\n", compile_stats.signatures.c_str());
+ printf("Hyperscan info: %s\n", compile_stats.db_info.c_str());
+ printf("Expression count: %'zu\n", compile_stats.expressionCount);
+ printf("Bytecode size: %'zu bytes\n", compile_stats.compiledSize);
+ printf("Database CRC: 0x%x\n", compile_stats.crc32);
+ if (compile_stats.streaming) {
+ printf("Stream state size: %'zu bytes\n", compile_stats.streamSize);
+ }
+ printf("Scratch size: %'zu bytes\n", compile_stats.scratchSize);
+ printf("Compile time: %'0.3Lf seconds\n", compile_stats.compileSecs);
+ printf("Peak heap usage: %'u bytes\n", compile_stats.peakMemorySize);
+}
+
+void EngineHyperscan::sqlStats(SqlDB &sqldb) const {
+ ostringstream crc;
+ crc << "0x" << hex << compile_stats.crc32;
+
+ static const std::string Q =
+ "INSERT INTO Compile ("
+ "sigsName, signatures, dbInfo, exprCount, dbSize, crc, streaming,"
+ "streamSize, scratchSize, compileSecs, peakMemory) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)";
+
+ sqldb.insert_all(Q, compile_stats.sigs_name, compile_stats.signatures,
+ compile_stats.db_info, compile_stats.expressionCount,
+ compile_stats.compiledSize, crc.str(),
+ compile_stats.streaming ? "TRUE" : "FALSE",
+ compile_stats.streamSize, compile_stats.scratchSize,
+ compile_stats.compileSecs, compile_stats.peakMemorySize);
+}
+
+
static
unsigned makeModeFlags(ScanMode scan_mode) {
switch (scan_mode) {
std::unique_ptr<EngineHyperscan>
buildEngineHyperscan(const ExpressionMap &expressions, ScanMode scan_mode,
- const std::string &name, UNUSED const ue2::Grey &grey) {
+ const std::string &name, const std::string &sigs_name,
+ UNUSED const ue2::Grey &grey) {
if (expressions.empty()) {
assert(0);
return nullptr;
size_t streamSize = 0;
size_t scratchSize = 0;
unsigned int peakMemorySize = 0;
- unsigned int crc = 0;
std::string db_info;
unsigned int mode = makeModeFlags(scan_mode);
}
assert(compiledSize > 0);
- crc = db->crc32;
-
if (saveDatabases) {
saveDatabase(db, dbFilename(name, mode).c_str());
}
}
hs_free_scratch(scratch);
- // Output summary information.
- printf("Signatures: %s\n", name.c_str());
- printf("Hyperscan info: %s\n", db_info.c_str());
- printf("Expression count: %'zu\n", expressions.size());
- printf("Bytecode size: %'zu bytes\n", compiledSize);
- printf("Database CRC: 0x%x\n", crc);
- if (mode & HS_MODE_STREAM) {
- printf("Stream state size: %'zu bytes\n", streamSize);
+ // Collect summary information.
+ CompileStats cs;
+ cs.sigs_name = sigs_name;
+ if (!sigs_name.empty()) {
+ const auto pos = name.find_last_of('/');
+ cs.signatures = name.substr(pos + 1);
+ } else {
+ cs.signatures = name;
}
- printf("Scratch size: %'zu bytes\n", scratchSize);
- printf("Compile time: %'0.3Lf seconds\n", compileSecs);
- printf("Peak heap usage: %'u bytes\n", peakMemorySize);
-
- return ue2::make_unique<EngineHyperscan>(db);
+ cs.db_info = db_info;
+ cs.expressionCount = expressions.size();
+ cs.compiledSize = compiledSize;
+ cs.crc32 = db->crc32;
+ cs.streaming = mode & HS_MODE_STREAM;
+ cs.streamSize = streamSize;
+ cs.scratchSize = scratchSize;
+ cs.compileSecs = compileSecs;
+ cs.peakMemorySize = peakMemorySize;
+
+ return ue2::make_unique<EngineHyperscan>(db, std::move(cs));
}
#include "expressions.h"
#include "common.h"
+#include "sqldb.h"
#include "hs_runtime.h"
#include <memory>
+#include <string>
#include <vector>
/** Structure for the result of a single complete scan. */
unsigned int matches = 0; //!< Count of matches found.
};
+/** Infomation about the database compile */
+struct CompileStats {
+ std::string sigs_name;
+ std::string signatures;
+ std::string db_info;
+ size_t expressionCount = 0;
+ size_t compiledSize = 0;
+ uint32_t crc32 = 0;
+ bool streaming;
+ size_t streamSize = 0;
+ size_t scratchSize = 0;
+ long double compileSecs = 0;
+ unsigned int peakMemorySize = 0;
+};
+
/** Engine context which is allocated on a per-thread basis. */
class EngineContext {
public:
/** Hyperscan Engine for scanning data. */
class EngineHyperscan {
public:
- explicit EngineHyperscan(hs_database_t *db);
+ explicit EngineHyperscan(hs_database_t *db, CompileStats cs);
~EngineHyperscan();
std::unique_ptr<EngineContext> makeContext() const;
void streamScan(EngineStream &stream, const char *data, unsigned int len,
unsigned int id, ResultEntry &result) const;
+ void printStats() const;
+
+ void sqlStats(SqlDB &db) const;
+
private:
hs_database_t *db;
+ CompileStats compile_stats;
};
namespace ue2 {
std::unique_ptr<EngineHyperscan>
buildEngineHyperscan(const ExpressionMap &expressions, ScanMode scan_mode,
- const std::string &name, const ue2::Grey &grey);
+ const std::string &name, const std::string &sigs_name,
+ const ue2::Grey &grey);
#endif // ENGINEHYPERSCAN_H
#include "data_corpus.h"
#include "engine_hyperscan.h"
#include "expressions.h"
+#include "sqldb.h"
#include "thread_barrier.h"
#include "timer.h"
#include "util/expression_path.h"
unsigned repeats = 20;
string exprPath("");
string corpusFile("");
+string sqloutFile("");
+string sigName(""); // info only
vector<unsigned int> threadCores;
Timer totalTimer;
double totalSecs = 0;
+SqlDB out_db;
+
typedef void (*thread_func_t)(void *context);
class ThreadContext : boost::noncopyable {
printf("\n");
printf(" --per-scan Display per-scan Mbit/sec results.\n");
printf(" --echo-matches Display all matches that occur during scan.\n");
+ printf(" --sql-out FILE Output sqlite db.\n");
+ printf(" -S NAME Signature set name (for sqlite db).\n");
printf("\n\n");
if (error) {
static
void processArgs(int argc, char *argv[], vector<BenchmarkSigs> &sigSets,
UNUSED unique_ptr<Grey> &grey) {
- const char options[] = "-b:c:Cd:e:E:G:hi:n:No:p:sVw:z:"
+ const char options[] = "-b:c:Cd:e:E:G:hi:n:No:p:sS:Vw:z:"
#ifdef HAVE_DECL_PTHREAD_SETAFFINITY_NP
"T:" // add the thread flag
#endif
;
int in_sigfile = 0;
int do_per_scan = 0;
- int do_echo_matches = 0;
int do_compress = 0;
int do_compress_size = 0;
+ int do_echo_matches = 0;
+ int do_sql_output = 0;
+ int option_index = 0;
vector<string> sigFiles;
static struct option longopts[] = {
- {"per-scan", 0, &do_per_scan, 1},
- {"echo-matches", 0, &do_echo_matches, 1},
- {"compress-stream", 0, &do_compress, 1},
- {"print-compress-size", 0, &do_compress_size, 1},
+ {"per-scan", no_argument, &do_per_scan, 1},
+ {"echo-matches", no_argument, &do_echo_matches, 1},
+ {"compress-stream", no_argument, &do_compress, 1},
+ {"sql-out", required_argument, &do_sql_output, 1},
{nullptr, 0, nullptr, 0}
};
for (;;) {
- int c = getopt_long(argc, argv, options, longopts, nullptr);
+ int c = getopt_long(argc, argv, options, longopts, &option_index);
if (c < 0) {
break;
}
case 'V':
scan_mode = ScanMode::VECTORED;
break;
+ case 'S':
+ sigName.assign(optarg);
+ break;
#ifdef HAVE_DECL_PTHREAD_SETAFFINITY_NP
case 'T':
if (!strToList(optarg, threadCores)) {
saveDatabases = true;
serializePath = optarg;
break;
+ case 0:
+ if (do_sql_output) {
+ sqloutFile.assign(optarg);
+ do_sql_output = 0;
+ }
+ break;
case 1:
if (in_sigfile) {
sigFiles.push_back(optarg);
in_sigfile = 2;
break;
}
- case 0:
- break;
+ /* fallthrough */
default:
usage("Unrecognised command line argument.");
exit(1);
}
}
+/** Dump per-scan throughput data to sql. */
+static
+void sqlPerScanResults(const vector<unique_ptr<ThreadContext>> &threads,
+ u64a bytesPerRun, u64a scan_id) {
+ static const std::string Q =
+ "INSERT INTO ScanResults (scan_id, thread, scan, throughput) "
+ "VALUES (?1, ?2, ?3, ?4)";
+
+ for (const auto &t : threads) {
+ const auto &results = t->results;
+ for (size_t j = 0; j != results.size(); j++) {
+ const auto &r = results[j];
+ double mbps = calc_mbps(r.seconds, bytesPerRun);
+ out_db.insert_all(Q, scan_id, t->num, j, mbps);
+ }
+ }
+}
+
+/** Dump benchmark results to sql. */
+static
+void sqlResults(const vector<unique_ptr<ThreadContext>> &threads,
+ const vector<DataBlock> &corpus_blocks) {
+ u64a bytesPerRun = byte_size(corpus_blocks);
+ u64a matchesPerRun = threads[0]->results[0].matches;
+
+ u64a scan_id = out_db.lastRowId();
+
+ // Sanity check: all of our results should have the same match count.
+ for (const auto &t : threads) {
+ if (!all_of(begin(t->results), end(t->results),
+ [&matchesPerRun](const ResultEntry &e) {
+ return e.matches == matchesPerRun;
+ })) {
+ printf("\nWARNING: PER-SCAN MATCH COUNTS ARE INCONSISTENT!\n\n");
+ break;
+ }
+ }
+
+ u64a totalBytes = bytesPerRun * repeats * threads.size();
+ double matchRate = ((double)matchesPerRun * 1024) / bytesPerRun;
+
+ const auto pos = corpusFile.find_last_of('/');
+ const auto corpus = corpusFile.substr(pos + 1);
+
+ static const std::string Q =
+ "INSERT INTO Scan (scan_id, corpusFile, totalSecs, "
+ "bytesPerRun, blockSize, blockCount, totalBytes, "
+ "totalBlocks, matchesPerRun, matchRate, overallTput) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)";
+
+ out_db.insert_all(
+ Q, scan_id, corpus, totalSecs, bytesPerRun, corpus_blocks.size(),
+ scan_mode == ScanMode::BLOCK ? 1 : count_streams(corpus_blocks),
+ totalBytes, corpus_blocks.size() * repeats * threads.size(),
+ matchesPerRun, matchRate, calc_mbps(totalSecs, totalBytes));
+
+ if (display_per_scan) {
+ sqlPerScanResults(threads, bytesPerRun, scan_id);
+ }
+}
+
/**
* Construct a thread context for this scanning mode.
*
t->join();
}
- // Display global results.
- displayResults(threads, corpus_blocks);
+ if (sqloutFile.empty()) {
+ // Display global results.
+ displayResults(threads, corpus_blocks);
+ } else {
+ // write to sqlite file
+ sqlResults(threads, corpus_blocks);
+ out_db.exec("END");
+ }
}
-
} // namespace
/** Main driver. */
printf("Corpus data error: %s\n", e.msg.c_str());
return 1;
}
-
- for (const auto &s : sigSets) {
- auto exprMap = limitToSignatures(exprMapTemplate, s.sigs);
- if (exprMap.empty()) {
- continue;
+ try {
+ if (!sqloutFile.empty()) {
+ out_db.open(sqloutFile);
}
- auto engine = buildEngineHyperscan(exprMap, scan_mode, s.name, *grey);
- if (!engine) {
- printf("Error: expressions failed to compile.\n");
- exit(1);
- }
+ for (const auto &s : sigSets) {
+ auto exprMap = limitToSignatures(exprMapTemplate, s.sigs);
+ if (exprMap.empty()) {
+ continue;
+ }
+
+ auto engine = buildEngineHyperscan(exprMap, scan_mode, s.name,
+ sigName, *grey);
+ if (!engine) {
+ printf("Error: expressions failed to compile.\n");
+ exit(1);
+ }
- printf("\n");
+ if (sqloutFile.empty()) {
+ // Display global results.
+ engine->printStats();
+ printf("\n");
- runBenchmark(*engine, corpus_blocks);
+ } else {
+ out_db.exec("BEGIN");
+ engine->sqlStats(out_db);
+ }
+
+ runBenchmark(*engine, corpus_blocks);
+ }
+ } catch (const SqlFailure &f) {
+ cerr << f.message << '\n';
+ return -1;
}
return 0;
--- /dev/null
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "common.h"
+#include "sqldb.h"
+#include "ue2common.h"
+
+#include <cassert>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <sqlite3.h>
+
+using namespace std;
+
+namespace {
+
+static
+sqlite3 *initDB(const string &filename) {
+ sqlite3 *db;
+ int status;
+ status = sqlite3_open_v2(filename.c_str(), &db,
+ SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
+
+ if (status != SQLITE_OK) {
+ ostringstream oss;
+ oss << "Unable to open database '" << filename
+ << "': " << sqlite3_errmsg(db);
+ status = sqlite3_close(db);
+ assert(status == SQLITE_OK);
+ throw SqlFailure(oss.str());
+ }
+
+ // create tables
+ static const string c("CREATE TABLE Compile ("
+ "id INTEGER PRIMARY KEY,"
+ "sigsName TEXT, "
+ "signatures TEXT, "
+ "dbInfo TEXT, "
+ "exprCount INTEGER, "
+ "dbSize INTEGER,"
+ "crc TEXT, "
+ "streaming TEXT, "
+ "streamSize INTEGER, "
+ "scratchSize INTEGER, "
+ "compileSecs DOUBLE, "
+ "peakMemory INTEGER"
+ ");");
+
+ static const string s("CREATE TABLE Scan (id INTEGER PRIMARY KEY,"
+ "corpusFile TEXT, scan_id INTEGER, "
+ "totalSecs DOUBLE, bytesPerRun INTEGER, "
+ "blockSize INTEGER, blockCount INTEGER, "
+ "totalBytes INTEGER, totalBlocks INTEGER, "
+ "matchesPerRun INTEGER, "
+ "matchRate DOUBLE, overallTput DOUBLE);");
+
+ static const string sr(
+ "CREATE TABLE ScanResults ( id INTEGER PRIMARY KEY, "
+ "scan_id INTEGER, thread INTEGER, scan INTEGER, throughput DOUBLE );");
+
+ static const string create_query = c + s + sr;
+
+ sqlite3_stmt *statement;
+ const char *pzTail = create_query.c_str();
+
+ while (strlen(pzTail)) {
+ status =
+ sqlite3_prepare(db, pzTail, strlen(pzTail), &statement, &pzTail);
+ if (status != SQLITE_OK) {
+ goto fail;
+ }
+ status = sqlite3_step(statement);
+ if (status != SQLITE_DONE && status != SQLITE_ROW) {
+ goto fail;
+ }
+ status = sqlite3_finalize(statement);
+ if (status != SQLITE_OK) {
+ goto fail;
+ }
+ }
+
+ return db;
+
+fail:
+ ostringstream oss;
+ oss << "Unable to create tables: " << sqlite3_errmsg(db);
+ status = sqlite3_close(db);
+ assert(status == SQLITE_OK);
+ throw SqlFailure(oss.str());
+}
+} // namespace
+
+SqlDB::~SqlDB() {
+ if (db) {
+ sqlite3_close(db);
+ }
+ db = nullptr;
+}
+
+void SqlDB::open(const string &filename) {
+ if (!ifstream(filename)) {
+ // file doesn't exist, go set up some tables
+ db = initDB(filename);
+ } else {
+ int status;
+ status = sqlite3_open_v2(filename.c_str(), &db, SQLITE_OPEN_READWRITE,
+ nullptr);
+
+ if (status != SQLITE_OK) {
+ ostringstream oss;
+ oss << "Unable to open database '" << filename
+ << "': " << sqlite3_errmsg(db);
+ throw SqlFailure(oss.str());
+ }
+ }
+
+ exec("PRAGMA synchronous = off;");
+ exec("PRAGMA encoding = 'UTF-8';");
+}
+
+void SqlDB::exec(const string &query) {
+ assert(db);
+ int status;
+ status = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr);
+ if (status != SQLITE_OK) {
+ ostringstream oss;
+ oss << "Unable to run sqlite query: " << sqlite3_errmsg(db);
+ sqlite3_close(db);
+ throw SqlFailure(oss.str());
+ }
+}
+
+u64a SqlDB::lastRowId() {
+ assert(db);
+ return sqlite3_last_insert_rowid(db);
+}
--- /dev/null
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SQLDB_H_
+#define SQLDB_H_
+
+#include "ue2common.h"
+
+#include "common.h"
+#include "sqldb_bind.h"
+
+#include <iostream>
+#include <string>
+
+#include <sqlite3.h>
+
+class SqlDB {
+public:
+ SqlDB() : db(nullptr) {};
+ ~SqlDB();
+ void open(const std::string &filename);
+ void exec(const std::string &query);
+ u64a lastRowId();
+
+ template <typename... Args>
+ void insert_all(const std::string &query, Args&&... args) {
+ sqlite3_stmt *stmt;
+ const char *tail;
+
+ int rc = sqlite3_prepare(db, query.c_str(), query.size(), &stmt, &tail);
+ if (rc != SQLITE_OK) {
+ std::ostringstream oss;
+ oss << "Unable to prepare query: " << sqlite3_errmsg(db);
+ throw SqlFailure(oss.str());
+ }
+
+ // only one statement per function call
+ assert(strlen(tail) == 0);
+
+ // perform templated binds to this statement
+ ue2_sqlite::bind_args(stmt, 1, args...);
+
+ rc = sqlite3_step(stmt);
+ if (rc != SQLITE_DONE) {
+ std::ostringstream oss;
+ oss << "Unable to run insert: " << sqlite3_errmsg(db);
+ throw SqlFailure(oss.str());
+ }
+
+ rc = sqlite3_finalize(stmt);
+ if (rc != SQLITE_OK) {
+ std::ostringstream oss;
+ oss << "Unable to finalize statement: " << sqlite3_errmsg(db);
+ throw SqlFailure(oss.str());
+ }
+ }
+
+private:
+ sqlite3 *db;
+};
+
+#endif /* SQLDB_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SQLDB_BIND_H_
+#define SQLDB_BIND_H_
+
+#include "ue2common.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <sqlite3.h>
+
+namespace ue2_sqlite {
+
+inline
+int bind_impl(sqlite3_stmt *stmt, int param, const unsigned long &val) {
+ return sqlite3_bind_int64(stmt, param, val);
+}
+
+inline
+int bind_impl(sqlite3_stmt *stmt, int param, const unsigned int &val) {
+ return sqlite3_bind_int(stmt, param, val);
+}
+
+inline
+int bind_impl(sqlite3_stmt *stmt, int param, const u64a &val) {
+ return sqlite3_bind_int64(stmt, param, val);
+}
+
+inline
+int bind_impl(sqlite3_stmt *stmt, int param, const double &val) {
+ return sqlite3_bind_double(stmt, param, val);
+}
+
+inline
+int bind_impl(sqlite3_stmt *stmt, int param, const long double &val) {
+ return sqlite3_bind_double(stmt, param, val);
+}
+
+inline
+int bind_impl(sqlite3_stmt *stmt, int param, const std::string &val) {
+ return sqlite3_bind_text(stmt, param, val.c_str(), val.size(),
+ SQLITE_TRANSIENT);
+}
+
+template<typename T>
+void bind_args(sqlite3_stmt *stmt, int param, T obj) {
+ int rc = bind_impl(stmt, param, obj);
+ if (rc != SQLITE_OK) {
+ std::ostringstream oss;
+ oss << "SQL value bind failed for param #: " << param;
+ throw SqlFailure(oss.str());
+ }
+}
+
+template<typename T, typename... Args>
+void bind_args(sqlite3_stmt *stmt, int param, T obj, Args&&... args) {
+ bind_args(stmt, param, obj);
+ bind_args(stmt, param + 1, args...);
+}
+
+} // namespace
+
+#endif /* SQLDB_BIND_H_ */