mirror of https://github.com/tstack/lnav
parent
906494ebfa
commit
f9f797fc9d
@ -0,0 +1,52 @@
|
||||
{
|
||||
"$id": "https://lnav.org/event-log-msg-detected-v1.schema.json",
|
||||
"title": "https://lnav.org/event-log-msg-detected-v1.schema.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Event fired when a log message is detected by a watch expression.",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"title": "/$schema",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"https://lnav.org/event-log-msg-detected-v1.schema.json"
|
||||
]
|
||||
},
|
||||
"watch-name": {
|
||||
"title": "/watch-name",
|
||||
"description": "The name of the watch expression that matched this log message",
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"title": "/filename",
|
||||
"description": "The path of the file containing the log message",
|
||||
"type": "string"
|
||||
},
|
||||
"format": {
|
||||
"title": "/format",
|
||||
"description": "The name of the log format that matched this log message",
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"title": "/timestamp",
|
||||
"description": "The timestamp of the log message",
|
||||
"type": "string"
|
||||
},
|
||||
"values": {
|
||||
"description": "The log message values captured by the log format",
|
||||
"title": "/values",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"([\\w\\-]+)": {
|
||||
"title": "/values/<name>",
|
||||
"type": [
|
||||
"boolean",
|
||||
"integer",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
@ -0,0 +1,355 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 Timothy Stack 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 REGENTS 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 REGENTS 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 "log.watch.hh"
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "base/injector.hh"
|
||||
#include "bound_tags.hh"
|
||||
#include "lnav.events.hh"
|
||||
#include "lnav_config_fwd.hh"
|
||||
#include "log_format.hh"
|
||||
#include "logfile_sub_source.cfg.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "sql_util.hh"
|
||||
|
||||
namespace lnav {
|
||||
namespace log {
|
||||
namespace watch {
|
||||
|
||||
struct compiled_watch_expr {
|
||||
auto_mem<sqlite3_stmt> cwe_stmt{sqlite3_finalize};
|
||||
bool cwe_enabled{true};
|
||||
};
|
||||
|
||||
struct expressions : public lnav_config_listener {
|
||||
void reload_config(error_reporter& reporter) override
|
||||
{
|
||||
auto& lnav_db = injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
|
||||
sqlite_db_tag>();
|
||||
|
||||
if (lnav_db.in() == nullptr) {
|
||||
log_warning("db not initialized yet!");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& cfg = injector::get<const logfile_sub_source_ns::config&>();
|
||||
|
||||
this->e_watch_exprs.clear();
|
||||
for (const auto& pair : cfg.c_watch_exprs) {
|
||||
auto stmt_str = fmt::format(FMT_STRING("SELECT 1 WHERE {}"),
|
||||
pair.second.we_expr);
|
||||
compiled_watch_expr cwe;
|
||||
|
||||
log_info("preparing watch expression: %s", stmt_str.c_str());
|
||||
auto retcode = sqlite3_prepare_v2(lnav_db,
|
||||
stmt_str.c_str(),
|
||||
stmt_str.size(),
|
||||
cwe.cwe_stmt.out(),
|
||||
nullptr);
|
||||
if (retcode != SQLITE_OK) {
|
||||
auto sql_al = attr_line_t(pair.second.we_expr)
|
||||
.with_attr_for_all(SA_PREFORMATTED.value())
|
||||
.with_attr_for_all(
|
||||
VC_ROLE.value(role_t::VCR_QUOTED_CODE));
|
||||
readline_sqlite_highlighter(sql_al, -1);
|
||||
intern_string_t watch_expr_path = intern_string::lookup(
|
||||
fmt::format(FMT_STRING("/log/watch-expressions/{}/expr"),
|
||||
pair.first));
|
||||
auto snippet = lnav::console::snippet::from(
|
||||
source_location(watch_expr_path), sql_al);
|
||||
|
||||
auto um = lnav::console::user_message::error(
|
||||
"SQL expression is invalid")
|
||||
.with_reason(sqlite3_errmsg(lnav_db))
|
||||
.with_snippet(snippet);
|
||||
|
||||
reporter(&pair.second.we_expr, um);
|
||||
continue;
|
||||
}
|
||||
|
||||
this->e_watch_exprs.emplace(pair.first, std::move(cwe));
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, compiled_watch_expr> e_watch_exprs;
|
||||
};
|
||||
|
||||
static expressions exprs;
|
||||
|
||||
void
|
||||
eval_with(logfile& lf, logfile::iterator ll)
|
||||
{
|
||||
if (std::none_of(exprs.e_watch_exprs.begin(),
|
||||
exprs.e_watch_exprs.end(),
|
||||
[](const auto& elem) { return elem.second.cwe_enabled; }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static auto& lnav_db
|
||||
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
|
||||
sqlite_db_tag>();
|
||||
|
||||
char timestamp_buffer[64] = "";
|
||||
shared_buffer_ref sbr, raw_sbr;
|
||||
lf.read_full_message(ll, sbr);
|
||||
auto format = lf.get_format();
|
||||
string_attrs_t sa;
|
||||
std::vector<logline_value> values;
|
||||
auto line_number = std::distance(lf.begin(), ll);
|
||||
format->annotate(line_number, sbr, sa, values);
|
||||
|
||||
for (auto& watch_pair : exprs.e_watch_exprs) {
|
||||
if (!watch_pair.second.cwe_enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* stmt = watch_pair.second.cwe_stmt.in();
|
||||
sqlite3_reset(stmt);
|
||||
|
||||
auto count = sqlite3_bind_parameter_count(stmt);
|
||||
auto missing_column = false;
|
||||
for (int lpc = 0; lpc < count; lpc++) {
|
||||
const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
|
||||
|
||||
if (name[0] == '$') {
|
||||
const char* env_value;
|
||||
|
||||
if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(
|
||||
stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_level") == 0) {
|
||||
sqlite3_bind_text(
|
||||
stmt, lpc + 1, ll->get_level_name(), -1, SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_time") == 0) {
|
||||
auto len = sql_strftime(timestamp_buffer,
|
||||
sizeof(timestamp_buffer),
|
||||
ll->get_timeval(),
|
||||
'T');
|
||||
sqlite3_bind_text(
|
||||
stmt, lpc + 1, timestamp_buffer, len, SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_time_msecs") == 0) {
|
||||
sqlite3_bind_int64(stmt, lpc + 1, ll->get_time_in_millis());
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_format") == 0) {
|
||||
const auto format_name = format->get_name();
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
format_name.get(),
|
||||
format_name.size(),
|
||||
SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_format_regex") == 0) {
|
||||
const auto pat_name = format->get_pattern_name(line_number);
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
pat_name.get(),
|
||||
pat_name.size(),
|
||||
SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_path") == 0) {
|
||||
const auto& filename = lf.get_filename();
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
filename.c_str(),
|
||||
filename.length(),
|
||||
SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_unique_path") == 0) {
|
||||
const auto& filename = lf.get_unique_path();
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
filename.c_str(),
|
||||
filename.length(),
|
||||
SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_text") == 0) {
|
||||
sqlite3_bind_text(
|
||||
stmt, lpc + 1, sbr.get_data(), sbr.length(), SQLITE_STATIC);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_body") == 0) {
|
||||
auto body_attr_opt = get_string_attr(sa, SA_BODY);
|
||||
if (body_attr_opt) {
|
||||
const auto& sar
|
||||
= body_attr_opt.value().saw_string_attr->sa_range;
|
||||
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
sbr.get_data_at(sar.lr_start),
|
||||
sar.length(),
|
||||
SQLITE_STATIC);
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_opid") == 0) {
|
||||
auto opid_attr_opt = get_string_attr(sa, logline::L_OPID);
|
||||
if (opid_attr_opt) {
|
||||
const auto& sar
|
||||
= opid_attr_opt.value().saw_string_attr->sa_range;
|
||||
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
sbr.get_data_at(sar.lr_start),
|
||||
sar.length(),
|
||||
SQLITE_STATIC);
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ":log_raw_text") == 0) {
|
||||
auto res = lf.read_raw_message(ll);
|
||||
|
||||
if (res.isOk()) {
|
||||
raw_sbr = res.unwrap();
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
raw_sbr.get_data(),
|
||||
raw_sbr.length(),
|
||||
SQLITE_STATIC);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto found = false;
|
||||
for (const auto& lv : values) {
|
||||
if (lv.lv_meta.lvm_name != &name[1]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
switch (lv.lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_BOOLEAN:
|
||||
sqlite3_bind_int64(stmt, lpc + 1, lv.lv_value.i);
|
||||
break;
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
sqlite3_bind_double(stmt, lpc + 1, lv.lv_value.d);
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
sqlite3_bind_int64(stmt, lpc + 1, lv.lv_value.i);
|
||||
break;
|
||||
case value_kind_t::VALUE_NULL:
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
break;
|
||||
default:
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
lv.text_value(),
|
||||
lv.text_length(),
|
||||
SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
missing_column = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (missing_column) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto step_res = sqlite3_step(stmt);
|
||||
|
||||
switch (step_res) {
|
||||
case SQLITE_OK:
|
||||
case SQLITE_DONE:
|
||||
continue;
|
||||
case SQLITE_ROW:
|
||||
break;
|
||||
default: {
|
||||
log_error("failed to execute watch expression: %s -- %s",
|
||||
watch_pair.first.c_str(),
|
||||
sqlite3_errmsg(lnav_db));
|
||||
watch_pair.second.cwe_enabled = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!timestamp_buffer[0]) {
|
||||
sql_strftime(timestamp_buffer,
|
||||
sizeof(timestamp_buffer),
|
||||
ll->get_timeval(),
|
||||
'T');
|
||||
}
|
||||
auto lmd = lnav::events::log::msg_detected{
|
||||
watch_pair.first,
|
||||
lf.get_filename(),
|
||||
lf.get_format_name().to_string(),
|
||||
timestamp_buffer,
|
||||
};
|
||||
for (const auto& lv : values) {
|
||||
switch (lv.lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_NULL:
|
||||
lmd.md_values[lv.lv_meta.lvm_name.to_string()]
|
||||
= json_null_t{};
|
||||
break;
|
||||
case value_kind_t::VALUE_BOOLEAN:
|
||||
lmd.md_values[lv.lv_meta.lvm_name.to_string()]
|
||||
= lv.lv_value.i ? true : false;
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
lmd.md_values[lv.lv_meta.lvm_name.to_string()]
|
||||
= lv.lv_value.i;
|
||||
break;
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
lmd.md_values[lv.lv_meta.lvm_name.to_string()]
|
||||
= lv.lv_value.d;
|
||||
break;
|
||||
default:
|
||||
lmd.md_values[lv.lv_meta.lvm_name.to_string()]
|
||||
= lv.to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
lnav::events::publish(lnav_db, lmd);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace watch
|
||||
} // namespace log
|
||||
} // namespace lnav
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 Timothy Stack 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 REGENTS 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 REGENTS 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 lnav_log_watch_hh
|
||||
#define lnav_log_watch_hh
|
||||
|
||||
#include "logfile.hh"
|
||||
|
||||
namespace lnav {
|
||||
namespace log {
|
||||
namespace watch {
|
||||
|
||||
void eval_with(logfile& lf, logfile::iterator ll);
|
||||
|
||||
}
|
||||
} // namespace log
|
||||
} // namespace lnav
|
||||
|
||||
#endif
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 Timothy Stack 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 REGENTS 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 REGENTS 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 lnav_logfile_sub_source_cfg_hh
|
||||
#define lnav_logfile_sub_source_cfg_hh
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace logfile_sub_source_ns {
|
||||
|
||||
struct watch_expression {
|
||||
std::string we_expr;
|
||||
bool we_enabled{true};
|
||||
};
|
||||
|
||||
struct config {
|
||||
std::map<std::string, watch_expression> c_watch_exprs;
|
||||
};
|
||||
|
||||
} // namespace logfile_sub_source_ns
|
||||
|
||||
#endif
|
@ -0,0 +1,186 @@
|
||||
// -*- c++ -*-
|
||||
/******************************************************************************
|
||||
* arenaalloc.h
|
||||
*
|
||||
* Arena allocator based on the example logic provided by Nicolai Josuttis
|
||||
* and available at http://www.josuttis.com/libbook/examples.html.
|
||||
* This enhanced work is provided under the terms of the MIT license.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _ARENA_ALLOC_H
|
||||
#define _ARENA_ALLOC_H
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
// Define macro ARENA_ALLOC_DEBUG to enable some tracing of the allocator
|
||||
#include "arenaallocimpl.h"
|
||||
|
||||
namespace ArenaAlloc
|
||||
{
|
||||
|
||||
struct _newAllocatorImpl
|
||||
{
|
||||
// these two functions should be supported by a specialized
|
||||
// allocator for shared memory or another source of specialized
|
||||
// memory such as device mapped memory.
|
||||
void* allocate( size_t numBytes ) { return new char[ numBytes ]; }
|
||||
void deallocate( void* ptr ) { delete[]( (char*)ptr ); }
|
||||
};
|
||||
|
||||
template <class T,
|
||||
class AllocatorImpl = _newAllocatorImpl,
|
||||
class MemblockImpl = _memblockimpl<AllocatorImpl> >
|
||||
class Alloc {
|
||||
|
||||
private:
|
||||
MemblockImpl* m_impl;
|
||||
|
||||
public:
|
||||
// type definitions
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
// when containers are swapped, (i.e. vector.swap)
|
||||
// swap the allocators also. This was not specified in c++98
|
||||
// thus users of this code not using c++11 must
|
||||
// exercise caution when using the swap algorithm or
|
||||
// specialized swap member function. Specifically,
|
||||
// don't swap containers not sharing the same
|
||||
// allocator internal implementation in c++98. This is ok
|
||||
// in c++11.
|
||||
typedef std::true_type propagate_on_container_swap;
|
||||
|
||||
// container moves should move the allocator also.
|
||||
typedef std::true_type propagate_on_container_move_assignment;
|
||||
#endif
|
||||
|
||||
// rebind allocator to type U
|
||||
template <class U>
|
||||
struct rebind {
|
||||
typedef Alloc<U,AllocatorImpl,MemblockImpl> other;
|
||||
};
|
||||
|
||||
// return address of values
|
||||
pointer address (reference value) const {
|
||||
return &value;
|
||||
}
|
||||
const_pointer address (const_reference value) const {
|
||||
return &value;
|
||||
}
|
||||
|
||||
Alloc( std::size_t defaultSize = 32768, AllocatorImpl allocImpl = AllocatorImpl() ) throw():
|
||||
m_impl( MemblockImpl::create( defaultSize, allocImpl ) )
|
||||
{
|
||||
}
|
||||
|
||||
Alloc(const Alloc& src) throw():
|
||||
m_impl( src.m_impl )
|
||||
{
|
||||
m_impl->incrementRefCount();
|
||||
}
|
||||
|
||||
template <class U>
|
||||
Alloc (const Alloc<U,AllocatorImpl,MemblockImpl>& src) throw():
|
||||
m_impl( 0 )
|
||||
{
|
||||
MemblockImpl::assign( src, m_impl );
|
||||
m_impl->incrementRefCount();
|
||||
}
|
||||
|
||||
~Alloc() throw()
|
||||
{
|
||||
m_impl->decrementRefCount();
|
||||
}
|
||||
|
||||
// return maximum number of elements that can be allocated
|
||||
size_type max_size () const throw()
|
||||
{
|
||||
return std::numeric_limits<std::size_t>::max() / sizeof(T);
|
||||
}
|
||||
|
||||
// allocate but don't initialize num elements of type T
|
||||
pointer allocate (size_type num, const void* = 0)
|
||||
{
|
||||
return reinterpret_cast<pointer>( m_impl->allocate(num*sizeof(T)) );
|
||||
}
|
||||
|
||||
// initialize elements of allocated storage p with value value
|
||||
#if __cplusplus >= 201103L
|
||||
|
||||
// use c++11 style forwarding to construct the object
|
||||
template< typename P, typename... Args>
|
||||
void construct( P* obj, Args&&... args )
|
||||
{
|
||||
::new((void*) obj ) P( std::forward<Args>( args )... );
|
||||
}
|
||||
|
||||
template< typename P >
|
||||
void destroy( P* obj ) { obj->~P(); }
|
||||
|
||||
#else
|
||||
void construct (pointer p, const T& value)
|
||||
{
|
||||
new((void*)p)T(value);
|
||||
}
|
||||
void destroy (pointer p) { p->~T(); }
|
||||
#endif
|
||||
|
||||
// deallocate storage p of deleted elements
|
||||
void deallocate (pointer p, size_type num)
|
||||
{
|
||||
m_impl->deallocate( p );
|
||||
}
|
||||
|
||||
bool equals( const MemblockImpl * impl ) const
|
||||
{
|
||||
return impl == m_impl;
|
||||
}
|
||||
|
||||
bool operator == ( const Alloc& t2 ) const
|
||||
{
|
||||
return m_impl == t2.m_impl;
|
||||
}
|
||||
|
||||
friend MemblockImpl;
|
||||
|
||||
template< typename Other >
|
||||
bool operator == ( const Alloc< Other, AllocatorImpl, MemblockImpl >& t2 )
|
||||
{
|
||||
return t2.equals( m_impl );
|
||||
}
|
||||
|
||||
template< typename Other >
|
||||
bool operator != ( const Alloc< Other, AllocatorImpl, MemblockImpl >& t2 )
|
||||
{
|
||||
return !t2.equals( m_impl );
|
||||
}
|
||||
|
||||
// These are extension functions not required for an stl allocator
|
||||
size_t getNumAllocations() { return m_impl->getNumAllocations(); }
|
||||
size_t getNumDeallocations() { return m_impl->getNumDeallocations(); }
|
||||
size_t getNumBytesAllocated() { return m_impl->getNumBytesAllocated(); }
|
||||
};
|
||||
|
||||
template<typename A>
|
||||
template<typename T>
|
||||
void _memblockimpl<A>::assign( const Alloc<T,A, _memblockimpl<A> >& src, _memblockimpl<A> *& dest )
|
||||
{
|
||||
dest = const_cast<_memblockimpl<A>* >(src.m_impl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,286 @@
|
||||
// -*- c++ -*-
|
||||
/******************************************************************************
|
||||
** arenaallocimpl.h
|
||||
**
|
||||
** Internal implementation types of the arena allocator
|
||||
** MIT license
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _ARENA_ALLOC_IMPL_H
|
||||
#define _ARENA_ALLOC_IMPL_H
|
||||
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
namespace ArenaAlloc
|
||||
{
|
||||
|
||||
template< typename T, typename A, typename M >
|
||||
class Alloc;
|
||||
|
||||
// internal structure for tracking memory blocks
|
||||
template < typename AllocImpl >
|
||||
struct _memblock
|
||||
{
|
||||
// allocations are rounded up to a multiple of the size of this
|
||||
// struct to maintain proper alignment for any pointer and double
|
||||
// values stored in the allocation.
|
||||
// A future goal is to support even stricter alignment for example
|
||||
// to support cache alignment, special device dependent mappings,
|
||||
// or GPU ops.
|
||||
union _roundsize {
|
||||
double d;
|
||||
void* p;
|
||||
};
|
||||
|
||||
_memblock* m_next{nullptr}; // blocks kept link listed for cleanup at end
|
||||
std::size_t m_bufferSize; // size of the buffer
|
||||
std::size_t m_index; // index of next allocatable byte in the block
|
||||
char* m_buffer; // pointer to large block to allocate from
|
||||
|
||||
_memblock(std::size_t bufferSize, AllocImpl& allocImpl)
|
||||
: m_bufferSize(roundSize(bufferSize)), m_index(0),
|
||||
m_buffer(reinterpret_cast<char*>(allocImpl.allocate(
|
||||
bufferSize))) // this works b/c of order of decl
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t roundSize( std::size_t numBytes )
|
||||
{
|
||||
// this is subject to overflow. calling logic should not permit
|
||||
// an attempt to allocate a really massive size.
|
||||
// i.e. an attempt to allocate 10s of terabytes should be an error
|
||||
return ( ( numBytes + sizeof( _roundsize ) - 1 ) /
|
||||
sizeof( _roundsize ) ) * sizeof( _roundsize );
|
||||
}
|
||||
|
||||
char * allocate( std::size_t numBytes )
|
||||
{
|
||||
std::size_t roundedSize = roundSize( numBytes );
|
||||
if( roundedSize + m_index > m_bufferSize )
|
||||
return 0;
|
||||
|
||||
char * ptrToReturn = &m_buffer[ m_index ];
|
||||
m_index += roundedSize;
|
||||
return ptrToReturn;
|
||||
}
|
||||
|
||||
void dispose( AllocImpl& impl )
|
||||
{
|
||||
impl.deallocate( m_buffer );
|
||||
}
|
||||
|
||||
~_memblock()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template< typename AllocatorImpl, typename Derived >
|
||||
struct _memblockimplbase
|
||||
{
|
||||
AllocatorImpl m_alloc;
|
||||
std::size_t m_refCount; // when refs -> 0 delete this
|
||||
std::size_t m_defaultSize;
|
||||
|
||||
std::size_t m_numAllocate; // number of times allocate called
|
||||
std::size_t m_numDeallocate; // number of time deallocate called
|
||||
std::size_t m_numBytesAllocated; // A good estimate of amount of space used
|
||||
|
||||
_memblock<AllocatorImpl> * m_head;
|
||||
_memblock<AllocatorImpl> * m_current;
|
||||
|
||||
// round up 2 next power of 2 if not already
|
||||
// a power of 2
|
||||
std::size_t roundpow2( std::size_t value )
|
||||
{
|
||||
// note this works because subtracting 1 is equivalent to
|
||||
// inverting the lowest set bit and complementing any
|
||||
// bits lower than that. only a power of 2
|
||||
// will yield 0 in the following check
|
||||
if( 0 == ( value & ( value - 1 ) ) )
|
||||
return value; // already a power of 2
|
||||
|
||||
// fold t over itself. This will set all bits after the highest set bit of t to 1
|
||||
// who said bit twiddling wasn't practical?
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
value |= value >> 32;
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
_memblockimplbase( std::size_t defaultSize, AllocatorImpl& allocator ):
|
||||
m_alloc( allocator ),
|
||||
m_refCount( 1 ),
|
||||
m_defaultSize( defaultSize ),
|
||||
m_numAllocate( 0 ),
|
||||
m_numDeallocate( 0 ),
|
||||
m_numBytesAllocated( 0 ),
|
||||
m_head( 0 ),
|
||||
m_current( 0 )
|
||||
{
|
||||
if( m_defaultSize < 256 )
|
||||
{
|
||||
m_defaultSize = 256; // anything less is academic. a more practical size is 4k or more
|
||||
}
|
||||
else if ( m_defaultSize > 1024UL*1024*1024*16 )
|
||||
{
|
||||
// when this becomes a problem, this package has succeeded beyond my wildest expectations
|
||||
m_defaultSize = 1024UL*1024*1024*16;
|
||||
}
|
||||
|
||||
// for convenience block size should be a power of 2
|
||||
// round up to next power of 2
|
||||
m_defaultSize = roundpow2( m_defaultSize );
|
||||
allocateNewBlock( m_defaultSize );
|
||||
}
|
||||
|
||||
char * allocate( std::size_t numBytes )
|
||||
{
|
||||
char * ptrToReturn = m_current->allocate( numBytes );
|
||||
if( !ptrToReturn )
|
||||
{
|
||||
allocateNewBlock( numBytes > m_defaultSize / 2 ? roundpow2( numBytes*2 ) :
|
||||
m_defaultSize );
|
||||
|
||||
ptrToReturn = m_current->allocate( numBytes );
|
||||
}
|
||||
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "_memblockimpl=%p allocated %ld bytes at address=%p\n", this, numBytes, ptrToReturn );
|
||||
#endif
|
||||
|
||||
++ m_numAllocate;
|
||||
m_numBytesAllocated += numBytes; // does not account for the small overhead in tracking the allocation
|
||||
|
||||
return ptrToReturn;
|
||||
}
|
||||
|
||||
void allocateNewBlock( std::size_t blockSize )
|
||||
{
|
||||
_memblock<AllocatorImpl> * newBlock = new ( m_alloc.allocate( sizeof( _memblock<AllocatorImpl> ) ) )
|
||||
_memblock<AllocatorImpl>( blockSize, m_alloc );
|
||||
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "_memblockimplbase=%p allocating a new block of size=%ld\n", this, blockSize );
|
||||
#endif
|
||||
|
||||
if( m_head == 0 )
|
||||
{
|
||||
m_head = m_current = newBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_current->m_next = newBlock;
|
||||
m_current = newBlock;
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate( void * ptr )
|
||||
{
|
||||
++ m_numDeallocate;
|
||||
}
|
||||
|
||||
size_t getNumAllocations() { return m_numAllocate; }
|
||||
size_t getNumDeallocations() { return m_numDeallocate; }
|
||||
size_t getNumBytesAllocated() { return m_numBytesAllocated; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
_memblock<AllocatorImpl> * block = m_head;
|
||||
while( block )
|
||||
{
|
||||
_memblock<AllocatorImpl> * curr = block;
|
||||
block = block->m_next;
|
||||
curr->dispose( m_alloc );
|
||||
curr->~_memblock<AllocatorImpl>();
|
||||
m_alloc.deallocate( curr );
|
||||
}
|
||||
}
|
||||
|
||||
// The ref counting model does not permit the sharing of
|
||||
// this object across multiple threads unless an external locking mechanism is applied
|
||||
// to ensure the atomicity of the reference count.
|
||||
void incrementRefCount()
|
||||
{
|
||||
++m_refCount;
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "ref count on _memblockimplbase=%p incremented to %ld\n", this, m_refCount );
|
||||
#endif
|
||||
}
|
||||
|
||||
void decrementRefCount()
|
||||
{
|
||||
--m_refCount;
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "ref count on _memblockimplbase=%p decremented to %ld\n", this, m_refCount );
|
||||
#endif
|
||||
|
||||
if( m_refCount == 0 )
|
||||
{
|
||||
Derived::destroy( static_cast<Derived*>(this) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Each allocator points to an instance of _memblockimpl which
|
||||
// contains the list of _memblock objects and other tracking info
|
||||
// including a refcount.
|
||||
// This object is instantiated in space obtained from the allocator
|
||||
// implementation. The allocator implementation is the component
|
||||
// on which allocate/deallocate are called to obtain storage from.
|
||||
template< typename AllocatorImpl >
|
||||
struct _memblockimpl : public _memblockimplbase<AllocatorImpl, _memblockimpl<AllocatorImpl> >
|
||||
{
|
||||
private:
|
||||
|
||||
typedef struct _memblockimplbase< AllocatorImpl, _memblockimpl<AllocatorImpl> > base_t;
|
||||
friend struct _memblockimplbase< AllocatorImpl, _memblockimpl<AllocatorImpl> >;
|
||||
|
||||
// to get around some sticky access issues between Alloc<T1> and Alloc<T2> when sharing
|
||||
// the implementation.
|
||||
template <typename U, typename A, typename M >
|
||||
friend class Alloc;
|
||||
|
||||
template< typename T >
|
||||
static void assign( const Alloc<T,AllocatorImpl, _memblockimpl<AllocatorImpl> >& src,
|
||||
_memblockimpl *& dest );
|
||||
|
||||
static _memblockimpl<AllocatorImpl> * create( size_t defaultSize, AllocatorImpl& alloc )
|
||||
{
|
||||
return new ( alloc.allocate( sizeof( _memblockimpl ) ) ) _memblockimpl<AllocatorImpl>( defaultSize,
|
||||
alloc );
|
||||
}
|
||||
|
||||
static void destroy( _memblockimpl<AllocatorImpl> * objToDestroy )
|
||||
{
|
||||
AllocatorImpl allocImpl = objToDestroy->m_alloc;
|
||||
objToDestroy-> ~_memblockimpl<AllocatorImpl>();
|
||||
allocImpl.deallocate( objToDestroy );
|
||||
}
|
||||
|
||||
_memblockimpl( std::size_t defaultSize, AllocatorImpl& allocImpl ):
|
||||
_memblockimplbase<AllocatorImpl, _memblockimpl<AllocatorImpl> >( defaultSize, allocImpl )
|
||||
{
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "_memblockimpl=%p constructed with default size=%ld\n", this,
|
||||
base_t::m_defaultSize );
|
||||
#endif
|
||||
}
|
||||
|
||||
~_memblockimpl( )
|
||||
{
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "~memblockimpl() called on _memblockimpl=%p\n", this );
|
||||
#endif
|
||||
base_t::clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,184 @@
|
||||
// -*- c++ -*-
|
||||
/******************************************************************************
|
||||
** recyclealloc.h
|
||||
**
|
||||
** Arena allocator with some modest recycling of freed resources.
|
||||
** MIT license
|
||||
**
|
||||
*****************************************************************************/
|
||||
#ifndef _RECYCLE_ALLOC_H
|
||||
#define _RECYCLE_ALLOC_H
|
||||
|
||||
#include "arenaalloc.h"
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace ArenaAlloc
|
||||
{
|
||||
|
||||
// todo:
|
||||
// attempt refactor of boilerplate in _memblockimpl and _recycleallocimpl
|
||||
template< typename AllocatorImpl, uint16_t StepSize = 16, uint16_t NumBuckets = 256 >
|
||||
struct _recycleallocimpl : public _memblockimplbase<AllocatorImpl, _recycleallocimpl<AllocatorImpl> >
|
||||
{
|
||||
private:
|
||||
|
||||
static_assert( ( StepSize >= 16 && NumBuckets >= 16 ), "Min step size=16, Min num buckets=16" );
|
||||
static_assert( !( StepSize & ( StepSize - 1 ) ), "Step size must be a power of 2" );
|
||||
|
||||
struct _freeEntry
|
||||
{
|
||||
// note: order of declaration matters
|
||||
std::size_t m_size;
|
||||
_freeEntry * m_next;
|
||||
};
|
||||
|
||||
_freeEntry * m_buckets[ NumBuckets ]; // m_buckets[ NumBuckets - 1 ] is the oversize bucket
|
||||
|
||||
typedef struct _memblockimplbase< AllocatorImpl, _recycleallocimpl<AllocatorImpl> > base_t;
|
||||
friend struct _memblockimplbase< AllocatorImpl, _recycleallocimpl<AllocatorImpl> >;
|
||||
|
||||
// to get around some sticky access issues between Alloc<T1> and Alloc<T2> when sharing
|
||||
// the implementation.
|
||||
template <typename U, typename A, typename M >
|
||||
friend class Alloc;
|
||||
|
||||
template< typename T >
|
||||
static void assign( const Alloc<T,AllocatorImpl, _recycleallocimpl<AllocatorImpl> >& src,
|
||||
_recycleallocimpl *& dest )
|
||||
{
|
||||
dest = const_cast< _recycleallocimpl<AllocatorImpl>* >( src.m_impl );
|
||||
}
|
||||
|
||||
static _recycleallocimpl<AllocatorImpl> * create( std::size_t defaultSize, AllocatorImpl& alloc )
|
||||
{
|
||||
return new (
|
||||
alloc.allocate( sizeof( _recycleallocimpl ) ) ) _recycleallocimpl<AllocatorImpl>( defaultSize,
|
||||
alloc );
|
||||
}
|
||||
|
||||
static void destroy( _recycleallocimpl<AllocatorImpl> * objToDestroy )
|
||||
{
|
||||
AllocatorImpl allocImpl = objToDestroy->m_alloc;
|
||||
objToDestroy-> ~_recycleallocimpl<AllocatorImpl>();
|
||||
allocImpl.deallocate( objToDestroy );
|
||||
}
|
||||
|
||||
_recycleallocimpl( std::size_t defaultSize, AllocatorImpl& allocImpl ):
|
||||
_memblockimplbase<AllocatorImpl, _recycleallocimpl<AllocatorImpl> >( defaultSize, allocImpl )
|
||||
{
|
||||
memset( m_buckets, 0, sizeof( m_buckets ) );
|
||||
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "_recycleallocimpl=%p constructed with default size=%ld\n", this,
|
||||
base_t::m_defaultSize );
|
||||
#endif
|
||||
}
|
||||
|
||||
~_recycleallocimpl( )
|
||||
{
|
||||
#ifdef ARENA_ALLOC_DEBUG
|
||||
fprintf( stdout, "~_recycleallocimpl() called on _recycleallocimpl=%p\n", this );
|
||||
#endif
|
||||
base_t::clear();
|
||||
}
|
||||
|
||||
char * allocate( std::size_t numBytes )
|
||||
{
|
||||
|
||||
numBytes = ( (numBytes + sizeof( std::size_t ) + StepSize - 1) / StepSize ) * StepSize;
|
||||
|
||||
char * returnValue = allocateInternal( numBytes );
|
||||
if( !returnValue )
|
||||
{
|
||||
char * allocValue = base_t::allocate( numBytes );
|
||||
|
||||
if( !allocValue )
|
||||
return 0; //allocation failure
|
||||
|
||||
*((std::size_t*)allocValue ) = numBytes; // that includes the header
|
||||
return allocValue + sizeof( std::size_t );
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void deallocate( void * ptr )
|
||||
{
|
||||
deallocateInternal( reinterpret_cast<char*>(ptr) );
|
||||
base_t::deallocate( ptr ); // this is called b/c it is known this just updates stats
|
||||
}
|
||||
|
||||
char * allocateInternal( std::size_t numBytes )
|
||||
{
|
||||
// numBytes must already be rounded to a multiple of stepsize and have an
|
||||
// extra sizeof( std::size_t ) bytes tacked on for the header
|
||||
// pointer returned points sizeof( std::size_t ) bytes into the allocation
|
||||
// bucket 0 is always null in this scheme.
|
||||
|
||||
uint16_t bucketNumber = numBytes / StepSize;
|
||||
|
||||
if( bucketNumber > NumBuckets - 1 )
|
||||
bucketNumber = NumBuckets - 1; // oversize alloc
|
||||
|
||||
// search max 3 consecutive buckets for an item large enough.
|
||||
// in the oversize bucket and only in the oversize bucket,
|
||||
// search upto 3 items into the linked list for an entry
|
||||
// large enough for the specified size
|
||||
for( uint16_t bkt = bucketNumber, i = 0; i < 3 && bkt < NumBuckets; ++i, ++bkt )
|
||||
{
|
||||
if( m_buckets[ bkt ] )
|
||||
return allocateFrom( numBytes, m_buckets[ bkt ] );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char * allocateFrom( std::size_t numBytes, _freeEntry *& head )
|
||||
{
|
||||
_freeEntry * current = head;
|
||||
_freeEntry * prev = 0;
|
||||
|
||||
int count = 0;
|
||||
|
||||
while( current && count < 3 )
|
||||
{
|
||||
if( current->m_size >= numBytes )
|
||||
{
|
||||
if( prev == 0 )
|
||||
head = current->m_next;
|
||||
else
|
||||
prev->m_next = current->m_next;
|
||||
|
||||
return reinterpret_cast<char*>(¤t->m_next);
|
||||
}
|
||||
|
||||
++count;
|
||||
prev = current;
|
||||
current = current->m_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void deallocateInternal( char * ptr )
|
||||
{
|
||||
_freeEntry * v = reinterpret_cast< _freeEntry* >( ptr - sizeof( std::size_t ) );
|
||||
uint16_t bucketNumber = v->m_size / StepSize;
|
||||
|
||||
if( bucketNumber > NumBuckets - 1 )
|
||||
bucketNumber = NumBuckets - 1;
|
||||
|
||||
_freeEntry * next = m_buckets[ bucketNumber ];
|
||||
v->m_next = next;
|
||||
m_buckets[ bucketNumber ] = v;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template< typename T, typename Allocator = _newAllocatorImpl >
|
||||
using RecycleAlloc = Alloc< T, Allocator, _recycleallocimpl<Allocator> >;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,770 @@
|
||||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Copyright (C) 2020-2021 Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://www.xxhash.com
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
* @file xxh_x86dispatch.c
|
||||
*
|
||||
* Automatic dispatcher code for the @ref XXH3_family on x86-based targets.
|
||||
*
|
||||
* Optional add-on.
|
||||
*
|
||||
* **Compile this file with the default flags for your target.** Do not compile
|
||||
* with flags like `-mavx*`, `-march=native`, or `/arch:AVX*`, there will be
|
||||
* an error. See @ref XXH_X86DISPATCH_ALLOW_AVX for details.
|
||||
*
|
||||
* @defgroup dispatch x86 Dispatcher
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !(defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64))
|
||||
# error "Dispatching is currently only supported on x86 and x86_64."
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* @def XXH_X86DISPATCH_ALLOW_AVX
|
||||
* @brief Disables the AVX sanity check.
|
||||
*
|
||||
* Don't compile xxh_x86dispatch.c with options like `-mavx*`, `-march=native`,
|
||||
* or `/arch:AVX*`. It is intended to be compiled for the minimum target, and
|
||||
* it selectively enables SSE2, AVX2, and AVX512 when it is needed.
|
||||
*
|
||||
* Using this option _globally_ allows this feature, and therefore makes it
|
||||
* undefined behavior to execute on any CPU without said feature.
|
||||
*
|
||||
* Even if the source code isn't directly using AVX intrinsics in a function,
|
||||
* the compiler can still generate AVX code from autovectorization and by
|
||||
* "upgrading" SSE2 intrinsics to use the VEX prefixes (a.k.a. AVX128).
|
||||
*
|
||||
* Use the same flags that you use to compile the rest of the program; this
|
||||
* file will safely generate SSE2, AVX2, and AVX512 without these flags.
|
||||
*
|
||||
* Define XXH_X86DISPATCH_ALLOW_AVX to ignore this check, and feel free to open
|
||||
* an issue if there is a target in the future where AVX is a default feature.
|
||||
*/
|
||||
#ifdef XXH_DOXYGEN
|
||||
# define XXH_X86DISPATCH_ALLOW_AVX
|
||||
#endif
|
||||
|
||||
#if defined(__AVX__) && !defined(XXH_X86DISPATCH_ALLOW_AVX)
|
||||
# error "Do not compile xxh_x86dispatch.c with AVX enabled! See the comment above."
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
# define XXH_HAS_INCLUDE(header) __has_include(header)
|
||||
#else
|
||||
# define XXH_HAS_INCLUDE(header) 0
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* @def XXH_DISPATCH_SCALAR
|
||||
* @brief Enables/dispatching the scalar code path.
|
||||
*
|
||||
* If this is defined to 0, SSE2 support is assumed. This reduces code size
|
||||
* when the scalar path is not needed.
|
||||
*
|
||||
* This is automatically defined to 0 when...
|
||||
* - SSE2 support is enabled in the compiler
|
||||
* - Targeting x86_64
|
||||
* - Targeting Android x86
|
||||
* - Targeting macOS
|
||||
*/
|
||||
#ifndef XXH_DISPATCH_SCALAR
|
||||
# if defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) /* SSE2 on by default */ \
|
||||
|| defined(__x86_64__) || defined(_M_X64) /* x86_64 */ \
|
||||
|| defined(__ANDROID__) || defined(__APPLEv__) /* Android or macOS */
|
||||
# define XXH_DISPATCH_SCALAR 0 /* disable */
|
||||
# else
|
||||
# define XXH_DISPATCH_SCALAR 1
|
||||
# endif
|
||||
#endif
|
||||
/*!
|
||||
* @def XXH_DISPATCH_AVX2
|
||||
* @brief Enables/disables dispatching for AVX2.
|
||||
*
|
||||
* This is automatically detected if it is not defined.
|
||||
* - GCC 4.7 and later are known to support AVX2, but >4.9 is required for
|
||||
* to get the AVX2 intrinsics and typedefs without -mavx -mavx2.
|
||||
* - Visual Studio 2013 Update 2 and later are known to support AVX2.
|
||||
* - The GCC/Clang internal header `<avx2intrin.h>` is detected. While this is
|
||||
* not allowed to be included directly, it still appears in the builtin
|
||||
* include path and is detectable with `__has_include`.
|
||||
*
|
||||
* @see XXH_AVX2
|
||||
*/
|
||||
#ifndef XXH_DISPATCH_AVX2
|
||||
# if (defined(__GNUC__) && (__GNUC__ > 4)) /* GCC 5.0+ */ \
|
||||
|| (defined(_MSC_VER) && _MSC_VER >= 1900) /* VS 2015+ */ \
|
||||
|| (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 180030501) /* VS 2013 Update 2 */ \
|
||||
|| XXH_HAS_INCLUDE(<avx2intrin.h>) /* GCC/Clang internal header */
|
||||
# define XXH_DISPATCH_AVX2 1 /* enable dispatch towards AVX2 */
|
||||
# else
|
||||
# define XXH_DISPATCH_AVX2 0
|
||||
# endif
|
||||
#endif /* XXH_DISPATCH_AVX2 */
|
||||
|
||||
/*!
|
||||
* @def XXH_DISPATCH_AVX512
|
||||
* @brief Enables/disables dispatching for AVX512.
|
||||
*
|
||||
* Automatically detected if one of the following conditions is met:
|
||||
* - GCC 4.9 and later are known to support AVX512.
|
||||
* - Visual Studio 2017 and later are known to support AVX2.
|
||||
* - The GCC/Clang internal header `<avx512fintrin.h>` is detected. While this
|
||||
* is not allowed to be included directly, it still appears in the builtin
|
||||
* include path and is detectable with `__has_include`.
|
||||
*
|
||||
* @see XXH_AVX512
|
||||
*/
|
||||
#ifndef XXH_DISPATCH_AVX512
|
||||
# if (defined(__GNUC__) \
|
||||
&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))) /* GCC 4.9+ */ \
|
||||
|| (defined(_MSC_VER) && _MSC_VER >= 1910) /* VS 2017+ */ \
|
||||
|| XXH_HAS_INCLUDE(<avx512fintrin.h>) /* GCC/Clang internal header */
|
||||
# define XXH_DISPATCH_AVX512 1 /* enable dispatch towards AVX512 */
|
||||
# else
|
||||
# define XXH_DISPATCH_AVX512 0
|
||||
# endif
|
||||
#endif /* XXH_DISPATCH_AVX512 */
|
||||
|
||||
/*!
|
||||
* @def XXH_TARGET_SSE2
|
||||
* @brief Allows a function to be compiled with SSE2 intrinsics.
|
||||
*
|
||||
* Uses `__attribute__((__target__("sse2")))` on GCC to allow SSE2 to be used
|
||||
* even with `-mno-sse2`.
|
||||
*
|
||||
* @def XXH_TARGET_AVX2
|
||||
* @brief Like @ref XXH_TARGET_SSE2, but for AVX2.
|
||||
*
|
||||
* @def XXH_TARGET_AVX512
|
||||
* @brief Like @ref XXH_TARGET_SSE2, but for AVX512.
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
# include <emmintrin.h> /* SSE2 */
|
||||
# if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||
# include <immintrin.h> /* AVX2, AVX512F */
|
||||
# endif
|
||||
# define XXH_TARGET_SSE2 __attribute__((__target__("sse2")))
|
||||
# define XXH_TARGET_AVX2 __attribute__((__target__("avx2")))
|
||||
# define XXH_TARGET_AVX512 __attribute__((__target__("avx512f")))
|
||||
#elif defined(_MSC_VER)
|
||||
# include <intrin.h>
|
||||
# define XXH_TARGET_SSE2
|
||||
# define XXH_TARGET_AVX2
|
||||
# define XXH_TARGET_AVX512
|
||||
#else
|
||||
# error "Dispatching is currently not supported for your compiler."
|
||||
#endif
|
||||
|
||||
#ifdef XXH_DISPATCH_DEBUG
|
||||
/* debug logging */
|
||||
# include <stdio.h>
|
||||
# define XXH_debugPrint(str) { fprintf(stderr, "DEBUG: xxHash dispatch: %s \n", str); fflush(NULL); }
|
||||
#else
|
||||
# define XXH_debugPrint(str) ((void)0)
|
||||
# undef NDEBUG /* avoid redefinition */
|
||||
# define NDEBUG
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#define XXH_INLINE_ALL
|
||||
#define XXH_X86DISPATCH
|
||||
#include "xxhash.h"
|
||||
|
||||
/*
|
||||
* Support both AT&T and Intel dialects
|
||||
*
|
||||
* GCC doesn't convert AT&T syntax to Intel syntax, and will error out if
|
||||
* compiled with -masm=intel. Instead, it supports dialect switching with
|
||||
* curly braces: { AT&T syntax | Intel syntax }
|
||||
*
|
||||
* Clang's integrated assembler automatically converts AT&T syntax to Intel if
|
||||
* needed, making the dialect switching useless (it isn't even supported).
|
||||
*
|
||||
* Note: Comments are written in the inline assembly itself.
|
||||
*/
|
||||
#ifdef __clang__
|
||||
# define XXH_I_ATT(intel, att) att "\n\t"
|
||||
#else
|
||||
# define XXH_I_ATT(intel, att) "{" att "|" intel "}\n\t"
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Runs CPUID.
|
||||
*
|
||||
* @param eax , ecx The parameters to pass to CPUID, %eax and %ecx respectively.
|
||||
* @param abcd The array to store the result in, `{ eax, ebx, ecx, edx }`
|
||||
*/
|
||||
static void XXH_cpuid(xxh_u32 eax, xxh_u32 ecx, xxh_u32* abcd)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
__cpuidex(abcd, eax, ecx);
|
||||
#else
|
||||
xxh_u32 ebx, edx;
|
||||
# if defined(__i386__) && defined(__PIC__)
|
||||
__asm__(
|
||||
"# Call CPUID\n\t"
|
||||
"#\n\t"
|
||||
"# On 32-bit x86 with PIC enabled, we are not allowed to overwrite\n\t"
|
||||
"# EBX, so we use EDI instead.\n\t"
|
||||
XXH_I_ATT("mov edi, ebx", "movl %%ebx, %%edi")
|
||||
XXH_I_ATT("cpuid", "cpuid" )
|
||||
XXH_I_ATT("xchg edi, ebx", "xchgl %%ebx, %%edi")
|
||||
: "=D" (ebx),
|
||||
# else
|
||||
__asm__(
|
||||
"# Call CPUID\n\t"
|
||||
XXH_I_ATT("cpuid", "cpuid")
|
||||
: "=b" (ebx),
|
||||
# endif
|
||||
"+a" (eax), "+c" (ecx), "=d" (edx));
|
||||
abcd[0] = eax;
|
||||
abcd[1] = ebx;
|
||||
abcd[2] = ecx;
|
||||
abcd[3] = edx;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Modified version of Intel's guide
|
||||
* https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
|
||||
*/
|
||||
|
||||
#if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Runs `XGETBV`.
|
||||
*
|
||||
* While the CPU may support AVX2, the operating system might not properly save
|
||||
* the full YMM/ZMM registers.
|
||||
*
|
||||
* xgetbv is used for detecting this: Any compliant operating system will define
|
||||
* a set of flags in the xcr0 register indicating how it saves the AVX registers.
|
||||
*
|
||||
* You can manually disable this flag on Windows by running, as admin:
|
||||
*
|
||||
* bcdedit.exe /set xsavedisable 1
|
||||
*
|
||||
* and rebooting. Run the same command with 0 to re-enable it.
|
||||
*/
|
||||
static xxh_u64 XXH_xgetbv(void)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
return _xgetbv(0); /* min VS2010 SP1 compiler is required */
|
||||
#else
|
||||
xxh_u32 xcr0_lo, xcr0_hi;
|
||||
__asm__(
|
||||
"# Call XGETBV\n\t"
|
||||
"#\n\t"
|
||||
"# Older assemblers (e.g. macOS's ancient GAS version) don't support\n\t"
|
||||
"# the XGETBV opcode, so we encode it by hand instead.\n\t"
|
||||
"# See <https://github.com/asmjit/asmjit/issues/78> for details.\n\t"
|
||||
".byte 0x0f, 0x01, 0xd0\n\t"
|
||||
: "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
|
||||
return xcr0_lo | ((xxh_u64)xcr0_hi << 32);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#define XXH_SSE2_CPUID_MASK (1 << 26)
|
||||
#define XXH_OSXSAVE_CPUID_MASK ((1 << 26) | (1 << 27))
|
||||
#define XXH_AVX2_CPUID_MASK (1 << 5)
|
||||
#define XXH_AVX2_XGETBV_MASK ((1 << 2) | (1 << 1))
|
||||
#define XXH_AVX512F_CPUID_MASK (1 << 16)
|
||||
#define XXH_AVX512F_XGETBV_MASK ((7 << 5) | (1 << 2) | (1 << 1))
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Returns the best XXH3 implementation.
|
||||
*
|
||||
* Runs various CPUID/XGETBV tests to try and determine the best implementation.
|
||||
*
|
||||
* @return The best @ref XXH_VECTOR implementation.
|
||||
* @see XXH_VECTOR_TYPES
|
||||
*/
|
||||
static int XXH_featureTest(void)
|
||||
{
|
||||
xxh_u32 abcd[4];
|
||||
xxh_u32 max_leaves;
|
||||
int best = XXH_SCALAR;
|
||||
#if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||
xxh_u64 xgetbv_val;
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
xxh_u32 cpuid_supported;
|
||||
__asm__(
|
||||
"# For the sake of ruthless backwards compatibility, check if CPUID\n\t"
|
||||
"# is supported in the EFLAGS on i386.\n\t"
|
||||
"# This is not necessary on x86_64 - CPUID is mandatory.\n\t"
|
||||
"# The ID flag (bit 21) in the EFLAGS register indicates support\n\t"
|
||||
"# for the CPUID instruction. If a software procedure can set and\n\t"
|
||||
"# clear this flag, the processor executing the procedure supports\n\t"
|
||||
"# the CPUID instruction.\n\t"
|
||||
"# <https://c9x.me/x86/html/file_module_x86_id_45.html>\n\t"
|
||||
"#\n\t"
|
||||
"# Routine is from <https://wiki.osdev.org/CPUID>.\n\t"
|
||||
|
||||
"# Save EFLAGS\n\t"
|
||||
XXH_I_ATT("pushfd", "pushfl" )
|
||||
"# Store EFLAGS\n\t"
|
||||
XXH_I_ATT("pushfd", "pushfl" )
|
||||
"# Invert the ID bit in stored EFLAGS\n\t"
|
||||
XXH_I_ATT("xor dword ptr[esp], 0x200000", "xorl $0x200000, (%%esp)")
|
||||
"# Load stored EFLAGS (with ID bit inverted)\n\t"
|
||||
XXH_I_ATT("popfd", "popfl" )
|
||||
"# Store EFLAGS again (ID bit may or not be inverted)\n\t"
|
||||
XXH_I_ATT("pushfd", "pushfl" )
|
||||
"# eax = modified EFLAGS (ID bit may or may not be inverted)\n\t"
|
||||
XXH_I_ATT("pop eax", "popl %%eax" )
|
||||
"# eax = whichever bits were changed\n\t"
|
||||
XXH_I_ATT("xor eax, dword ptr[esp]", "xorl (%%esp), %%eax" )
|
||||
"# Restore original EFLAGS\n\t"
|
||||
XXH_I_ATT("popfd", "popfl" )
|
||||
"# eax = zero if ID bit can't be changed, else non-zero\n\t"
|
||||
XXH_I_ATT("and eax, 0x200000", "andl $0x200000, %%eax" )
|
||||
: "=a" (cpuid_supported) :: "cc");
|
||||
|
||||
if (XXH_unlikely(!cpuid_supported)) {
|
||||
XXH_debugPrint("CPUID support is not detected!");
|
||||
return best;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Check how many CPUID pages we have */
|
||||
XXH_cpuid(0, 0, abcd);
|
||||
max_leaves = abcd[0];
|
||||
|
||||
/* Shouldn't happen on hardware, but happens on some QEMU configs. */
|
||||
if (XXH_unlikely(max_leaves == 0)) {
|
||||
XXH_debugPrint("Max CPUID leaves == 0!");
|
||||
return best;
|
||||
}
|
||||
|
||||
/* Check for SSE2, OSXSAVE and xgetbv */
|
||||
XXH_cpuid(1, 0, abcd);
|
||||
|
||||
/*
|
||||
* Test for SSE2. The check is redundant on x86_64, but it doesn't hurt.
|
||||
*/
|
||||
if (XXH_unlikely((abcd[3] & XXH_SSE2_CPUID_MASK) != XXH_SSE2_CPUID_MASK))
|
||||
return best;
|
||||
|
||||
XXH_debugPrint("SSE2 support detected.");
|
||||
|
||||
best = XXH_SSE2;
|
||||
#if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||
/* Make sure we have enough leaves */
|
||||
if (XXH_unlikely(max_leaves < 7))
|
||||
return best;
|
||||
|
||||
/* Test for OSXSAVE and XGETBV */
|
||||
if ((abcd[2] & XXH_OSXSAVE_CPUID_MASK) != XXH_OSXSAVE_CPUID_MASK)
|
||||
return best;
|
||||
|
||||
/* CPUID check for AVX features */
|
||||
XXH_cpuid(7, 0, abcd);
|
||||
|
||||
xgetbv_val = XXH_xgetbv();
|
||||
#if XXH_DISPATCH_AVX2
|
||||
/* Validate that AVX2 is supported by the CPU */
|
||||
if ((abcd[1] & XXH_AVX2_CPUID_MASK) != XXH_AVX2_CPUID_MASK)
|
||||
return best;
|
||||
|
||||
/* Validate that the OS supports YMM registers */
|
||||
if ((xgetbv_val & XXH_AVX2_XGETBV_MASK) != XXH_AVX2_XGETBV_MASK) {
|
||||
XXH_debugPrint("AVX2 supported by the CPU, but not the OS.");
|
||||
return best;
|
||||
}
|
||||
|
||||
/* AVX2 supported */
|
||||
XXH_debugPrint("AVX2 support detected.");
|
||||
best = XXH_AVX2;
|
||||
#endif
|
||||
#if XXH_DISPATCH_AVX512
|
||||
/* Check if AVX512F is supported by the CPU */
|
||||
if ((abcd[1] & XXH_AVX512F_CPUID_MASK) != XXH_AVX512F_CPUID_MASK) {
|
||||
XXH_debugPrint("AVX512F not supported by CPU");
|
||||
return best;
|
||||
}
|
||||
|
||||
/* Validate that the OS supports ZMM registers */
|
||||
if ((xgetbv_val & XXH_AVX512F_XGETBV_MASK) != XXH_AVX512F_XGETBV_MASK) {
|
||||
XXH_debugPrint("AVX512F supported by the CPU, but not the OS.");
|
||||
return best;
|
||||
}
|
||||
|
||||
/* AVX512F supported */
|
||||
XXH_debugPrint("AVX512F support detected.");
|
||||
best = XXH_AVX512;
|
||||
#endif
|
||||
#endif
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
/* === Vector implementations === */
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Defines the various dispatch functions.
|
||||
*
|
||||
* TODO: Consolidate?
|
||||
*
|
||||
* @param suffix The suffix for the functions, e.g. sse2 or scalar
|
||||
* @param target XXH_TARGET_* or empty.
|
||||
*/
|
||||
#define XXH_DEFINE_DISPATCH_FUNCS(suffix, target) \
|
||||
\
|
||||
/* === XXH3, default variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH64_hash_t \
|
||||
XXHL64_default_##suffix(const void* XXH_RESTRICT input, size_t len) \
|
||||
{ \
|
||||
return XXH3_hashLong_64b_internal( \
|
||||
input, len, XXH3_kSecret, sizeof(XXH3_kSecret), \
|
||||
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
/* === XXH3, Seeded variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH64_hash_t \
|
||||
XXHL64_seed_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||
XXH64_hash_t seed) \
|
||||
{ \
|
||||
return XXH3_hashLong_64b_withSeed_internal( \
|
||||
input, len, seed, XXH3_accumulate_512_##suffix, \
|
||||
XXH3_scrambleAcc_##suffix, XXH3_initCustomSecret_##suffix \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
/* === XXH3, Secret variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH64_hash_t \
|
||||
XXHL64_secret_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||
const void* secret, size_t secretLen) \
|
||||
{ \
|
||||
return XXH3_hashLong_64b_internal( \
|
||||
input, len, secret, secretLen, \
|
||||
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
/* === XXH3 update variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH_errorcode \
|
||||
XXH3_update_##suffix(XXH3_state_t* state, const void* input, size_t len) \
|
||||
{ \
|
||||
return XXH3_update(state, (const xxh_u8*)input, len, \
|
||||
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix); \
|
||||
} \
|
||||
\
|
||||
/* === XXH128 default variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH128_hash_t \
|
||||
XXHL128_default_##suffix(const void* XXH_RESTRICT input, size_t len) \
|
||||
{ \
|
||||
return XXH3_hashLong_128b_internal( \
|
||||
input, len, XXH3_kSecret, sizeof(XXH3_kSecret), \
|
||||
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
/* === XXH128 Secret variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH128_hash_t \
|
||||
XXHL128_secret_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||
const void* XXH_RESTRICT secret, size_t secretLen) \
|
||||
{ \
|
||||
return XXH3_hashLong_128b_internal( \
|
||||
input, len, (const xxh_u8*)secret, secretLen, \
|
||||
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix); \
|
||||
} \
|
||||
\
|
||||
/* === XXH128 Seeded variants === */ \
|
||||
\
|
||||
XXH_NO_INLINE target XXH128_hash_t \
|
||||
XXHL128_seed_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||
XXH64_hash_t seed) \
|
||||
{ \
|
||||
return XXH3_hashLong_128b_withSeed_internal(input, len, seed, \
|
||||
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix, \
|
||||
XXH3_initCustomSecret_##suffix); \
|
||||
}
|
||||
|
||||
/* End XXH_DEFINE_DISPATCH_FUNCS */
|
||||
|
||||
#if XXH_DISPATCH_SCALAR
|
||||
XXH_DEFINE_DISPATCH_FUNCS(scalar, /* nothing */)
|
||||
#endif
|
||||
XXH_DEFINE_DISPATCH_FUNCS(sse2, XXH_TARGET_SSE2)
|
||||
#if XXH_DISPATCH_AVX2
|
||||
XXH_DEFINE_DISPATCH_FUNCS(avx2, XXH_TARGET_AVX2)
|
||||
#endif
|
||||
#if XXH_DISPATCH_AVX512
|
||||
XXH_DEFINE_DISPATCH_FUNCS(avx512, XXH_TARGET_AVX512)
|
||||
#endif
|
||||
#undef XXH_DEFINE_DISPATCH_FUNCS
|
||||
|
||||
/* ==== Dispatchers ==== */
|
||||
|
||||
typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_default)(const void* XXH_RESTRICT, size_t);
|
||||
|
||||
typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_withSeed)(const void* XXH_RESTRICT, size_t, XXH64_hash_t);
|
||||
|
||||
typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_withSecret)(const void* XXH_RESTRICT, size_t, const void* XXH_RESTRICT, size_t);
|
||||
|
||||
typedef XXH_errorcode (*XXH3_dispatchx86_update)(XXH3_state_t*, const void*, size_t);
|
||||
|
||||
typedef struct {
|
||||
XXH3_dispatchx86_hashLong64_default hashLong64_default;
|
||||
XXH3_dispatchx86_hashLong64_withSeed hashLong64_seed;
|
||||
XXH3_dispatchx86_hashLong64_withSecret hashLong64_secret;
|
||||
XXH3_dispatchx86_update update;
|
||||
} XXH_dispatchFunctions_s;
|
||||
|
||||
#define XXH_NB_DISPATCHES 4
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Table of dispatchers for @ref XXH3_64bits().
|
||||
*
|
||||
* @pre The indices must match @ref XXH_VECTOR_TYPE.
|
||||
*/
|
||||
static const XXH_dispatchFunctions_s XXH_kDispatch[XXH_NB_DISPATCHES] = {
|
||||
#if XXH_DISPATCH_SCALAR
|
||||
/* Scalar */ { XXHL64_default_scalar, XXHL64_seed_scalar, XXHL64_secret_scalar, XXH3_update_scalar },
|
||||
#else
|
||||
/* Scalar */ { NULL, NULL, NULL, NULL },
|
||||
#endif
|
||||
/* SSE2 */ { XXHL64_default_sse2, XXHL64_seed_sse2, XXHL64_secret_sse2, XXH3_update_sse2 },
|
||||
#if XXH_DISPATCH_AVX2
|
||||
/* AVX2 */ { XXHL64_default_avx2, XXHL64_seed_avx2, XXHL64_secret_avx2, XXH3_update_avx2 },
|
||||
#else
|
||||
/* AVX2 */ { NULL, NULL, NULL, NULL },
|
||||
#endif
|
||||
#if XXH_DISPATCH_AVX512
|
||||
/* AVX512 */ { XXHL64_default_avx512, XXHL64_seed_avx512, XXHL64_secret_avx512, XXH3_update_avx512 }
|
||||
#else
|
||||
/* AVX512 */ { NULL, NULL, NULL, NULL }
|
||||
#endif
|
||||
};
|
||||
/*!
|
||||
* @internal
|
||||
* @brief The selected dispatch table for @ref XXH3_64bits().
|
||||
*/
|
||||
static XXH_dispatchFunctions_s XXH_g_dispatch = { NULL, NULL, NULL, NULL };
|
||||
|
||||
|
||||
typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_default)(const void* XXH_RESTRICT, size_t);
|
||||
|
||||
typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_withSeed)(const void* XXH_RESTRICT, size_t, XXH64_hash_t);
|
||||
|
||||
typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_withSecret)(const void* XXH_RESTRICT, size_t, const void* XXH_RESTRICT, size_t);
|
||||
|
||||
typedef struct {
|
||||
XXH3_dispatchx86_hashLong128_default hashLong128_default;
|
||||
XXH3_dispatchx86_hashLong128_withSeed hashLong128_seed;
|
||||
XXH3_dispatchx86_hashLong128_withSecret hashLong128_secret;
|
||||
XXH3_dispatchx86_update update;
|
||||
} XXH_dispatch128Functions_s;
|
||||
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Table of dispatchers for @ref XXH3_128bits().
|
||||
*
|
||||
* @pre The indices must match @ref XXH_VECTOR_TYPE.
|
||||
*/
|
||||
static const XXH_dispatch128Functions_s XXH_kDispatch128[XXH_NB_DISPATCHES] = {
|
||||
#if XXH_DISPATCH_SCALAR
|
||||
/* Scalar */ { XXHL128_default_scalar, XXHL128_seed_scalar, XXHL128_secret_scalar, XXH3_update_scalar },
|
||||
#else
|
||||
/* Scalar */ { NULL, NULL, NULL, NULL },
|
||||
#endif
|
||||
/* SSE2 */ { XXHL128_default_sse2, XXHL128_seed_sse2, XXHL128_secret_sse2, XXH3_update_sse2 },
|
||||
#if XXH_DISPATCH_AVX2
|
||||
/* AVX2 */ { XXHL128_default_avx2, XXHL128_seed_avx2, XXHL128_secret_avx2, XXH3_update_avx2 },
|
||||
#else
|
||||
/* AVX2 */ { NULL, NULL, NULL, NULL },
|
||||
#endif
|
||||
#if XXH_DISPATCH_AVX512
|
||||
/* AVX512 */ { XXHL128_default_avx512, XXHL128_seed_avx512, XXHL128_secret_avx512, XXH3_update_avx512 }
|
||||
#else
|
||||
/* AVX512 */ { NULL, NULL, NULL, NULL }
|
||||
#endif
|
||||
};
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief The selected dispatch table for @ref XXH3_64bits().
|
||||
*/
|
||||
static XXH_dispatch128Functions_s XXH_g_dispatch128 = { NULL, NULL, NULL, NULL };
|
||||
|
||||
/*!
|
||||
* @internal
|
||||
* @brief Runs a CPUID check and sets the correct dispatch tables.
|
||||
*/
|
||||
static void XXH_setDispatch(void)
|
||||
{
|
||||
int vecID = XXH_featureTest();
|
||||
XXH_STATIC_ASSERT(XXH_AVX512 == XXH_NB_DISPATCHES-1);
|
||||
assert(XXH_SCALAR <= vecID && vecID <= XXH_AVX512);
|
||||
#if !XXH_DISPATCH_SCALAR
|
||||
assert(vecID != XXH_SCALAR);
|
||||
#endif
|
||||
#if !XXH_DISPATCH_AVX512
|
||||
assert(vecID != XXH_AVX512);
|
||||
#endif
|
||||
#if !XXH_DISPATCH_AVX2
|
||||
assert(vecID != XXH_AVX2);
|
||||
#endif
|
||||
XXH_g_dispatch = XXH_kDispatch[vecID];
|
||||
XXH_g_dispatch128 = XXH_kDispatch128[vecID];
|
||||
}
|
||||
|
||||
|
||||
/* ==== XXH3 public functions ==== */
|
||||
|
||||
static XXH64_hash_t
|
||||
XXH3_hashLong_64b_defaultSecret_selection(const void* input, size_t len,
|
||||
XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
|
||||
{
|
||||
(void)seed64; (void)secret; (void)secretLen;
|
||||
if (XXH_g_dispatch.hashLong64_default == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch.hashLong64_default(input, len);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_dispatch(const void* input, size_t len)
|
||||
{
|
||||
return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_defaultSecret_selection);
|
||||
}
|
||||
|
||||
static XXH64_hash_t
|
||||
XXH3_hashLong_64b_withSeed_selection(const void* input, size_t len,
|
||||
XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
|
||||
{
|
||||
(void)secret; (void)secretLen;
|
||||
if (XXH_g_dispatch.hashLong64_seed == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch.hashLong64_seed(input, len, seed64);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed)
|
||||
{
|
||||
return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed_selection);
|
||||
}
|
||||
|
||||
static XXH64_hash_t
|
||||
XXH3_hashLong_64b_withSecret_selection(const void* input, size_t len,
|
||||
XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
|
||||
{
|
||||
(void)seed64;
|
||||
if (XXH_g_dispatch.hashLong64_secret == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch.hashLong64_secret(input, len, secret, secretLen);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen)
|
||||
{
|
||||
return XXH3_64bits_internal(input, len, 0, secret, secretLen, XXH3_hashLong_64b_withSecret_selection);
|
||||
}
|
||||
|
||||
XXH_errorcode
|
||||
XXH3_64bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len)
|
||||
{
|
||||
if (XXH_g_dispatch.update == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch.update(state, (const xxh_u8*)input, len);
|
||||
}
|
||||
|
||||
|
||||
/* ==== XXH128 public functions ==== */
|
||||
|
||||
static XXH128_hash_t
|
||||
XXH3_hashLong_128b_defaultSecret_selection(const void* input, size_t len,
|
||||
XXH64_hash_t seed64, const void* secret, size_t secretLen)
|
||||
{
|
||||
(void)seed64; (void)secret; (void)secretLen;
|
||||
if (XXH_g_dispatch128.hashLong128_default == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch128.hashLong128_default(input, len);
|
||||
}
|
||||
|
||||
XXH128_hash_t XXH3_128bits_dispatch(const void* input, size_t len)
|
||||
{
|
||||
return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_defaultSecret_selection);
|
||||
}
|
||||
|
||||
static XXH128_hash_t
|
||||
XXH3_hashLong_128b_withSeed_selection(const void* input, size_t len,
|
||||
XXH64_hash_t seed64, const void* secret, size_t secretLen)
|
||||
{
|
||||
(void)secret; (void)secretLen;
|
||||
if (XXH_g_dispatch128.hashLong128_seed == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch128.hashLong128_seed(input, len, seed64);
|
||||
}
|
||||
|
||||
XXH128_hash_t XXH3_128bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed)
|
||||
{
|
||||
return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed_selection);
|
||||
}
|
||||
|
||||
static XXH128_hash_t
|
||||
XXH3_hashLong_128b_withSecret_selection(const void* input, size_t len,
|
||||
XXH64_hash_t seed64, const void* secret, size_t secretLen)
|
||||
{
|
||||
(void)seed64;
|
||||
if (XXH_g_dispatch128.hashLong128_secret == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch128.hashLong128_secret(input, len, secret, secretLen);
|
||||
}
|
||||
|
||||
XXH128_hash_t XXH3_128bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen)
|
||||
{
|
||||
return XXH3_128bits_internal(input, len, 0, secret, secretLen, XXH3_hashLong_128b_withSecret_selection);
|
||||
}
|
||||
|
||||
XXH_errorcode
|
||||
XXH3_128bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len)
|
||||
{
|
||||
if (XXH_g_dispatch128.update == NULL) XXH_setDispatch();
|
||||
return XXH_g_dispatch128.update(state, (const xxh_u8*)input, len);
|
||||
}
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
/*! @} */
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* xxHash - XXH3 Dispatcher for x86-based targets
|
||||
* Copyright (C) 2020-2021 Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://www.xxhash.com
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
#ifndef XXH_X86DISPATCH_H_13563687684
|
||||
#define XXH_X86DISPATCH_H_13563687684
|
||||
|
||||
#include "xxhash.h" /* XXH64_hash_t, XXH3_state_t */
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_dispatch(const void* input, size_t len);
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed);
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen);
|
||||
XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len);
|
||||
|
||||
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_dispatch(const void* input, size_t len);
|
||||
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed);
|
||||
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen);
|
||||
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* automatic replacement of XXH3 functions.
|
||||
* can be disabled by setting XXH_DISPATCH_DISABLE_REPLACE */
|
||||
#ifndef XXH_DISPATCH_DISABLE_REPLACE
|
||||
|
||||
# undef XXH3_64bits
|
||||
# define XXH3_64bits XXH3_64bits_dispatch
|
||||
# undef XXH3_64bits_withSeed
|
||||
# define XXH3_64bits_withSeed XXH3_64bits_withSeed_dispatch
|
||||
# undef XXH3_64bits_withSecret
|
||||
# define XXH3_64bits_withSecret XXH3_64bits_withSecret_dispatch
|
||||
# undef XXH3_64bits_update
|
||||
# define XXH3_64bits_update XXH3_64bits_update_dispatch
|
||||
|
||||
# undef XXH128
|
||||
# define XXH128 XXH3_128bits_withSeed_dispatch
|
||||
# undef XXH3_128bits
|
||||
# define XXH3_128bits XXH3_128bits_dispatch
|
||||
# undef XXH3_128bits_withSeed
|
||||
# define XXH3_128bits_withSeed XXH3_128bits_withSeed_dispatch
|
||||
# undef XXH3_128bits_withSecret
|
||||
# define XXH3_128bits_withSecret XXH3_128bits_withSecret_dispatch
|
||||
# undef XXH3_128bits_update
|
||||
# define XXH3_128bits_update XXH3_128bits_update_dispatch
|
||||
|
||||
#endif /* XXH_DISPATCH_DISABLE_REPLACE */
|
||||
|
||||
#endif /* XXH_X86DISPATCH_H_13563687684 */
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Copyright (C) 2012-2021 Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://www.xxhash.com
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* xxhash.c instantiates functions defined in xxhash.h
|
||||
*/
|
||||
|
||||
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
|
||||
#define XXH_IMPLEMENTATION /* access definitions */
|
||||
|
||||
#include "xxhash.h"
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
/log/watch-expressions = {
|
||||
"http-errors": {
|
||||
"expr": ":sc_status >= 400",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
/log/watch-expressions = {
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{"content":{"$schema":"https://lnav.org/event-file-open-v1.schema.json","filename":"{test_dir}/logfile_access_log.0"}}
|
||||
{"content":{"$schema":"https://lnav.org/event-file-format-detected-v1.schema.json","filename":"{test_dir}/logfile_access_log.0","format":"access_log"}}
|
||||
{"content":{"$schema":"https://lnav.org/event-log-msg-detected-v1.schema.json","watch-name":"http-errors","filename":"{test_dir}/logfile_access_log.0","format":"access_log","timestamp":"2009-07-20T22:59:29.000","values":{"body":"","c_ip":"192.168.202.254","cs_method":"GET","cs_referer":"-","cs_uri_query":null,"cs_uri_stem":"/vmw/vSphere/default/vmkboot.gz","cs_user_agent":"gPXE/0.9.7","cs_username":"-","cs_version":"HTTP/1.0","sc_bytes":46210,"sc_status":404,"timestamp":"20/Jul/2009:22:59:29 +0000"}}}
|
@ -1,6 +1,31 @@
|
||||
#! /bin/bash
|
||||
|
||||
rm -rf events-home
|
||||
mkdir -p events-home
|
||||
export HOME=events-home
|
||||
export YES_COLOR=1
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ';SELECT json(content) as content FROM lnav_events' \
|
||||
-c ':write-jsonlines-to -' \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-c ':config /log/watch-expressions/http-errors/expr sc_status >= 400 AND bad'
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-c ':config /log/watch-expressions/http-errors/expr :sc_status >= 400'
|
||||
|
||||
run_cap_test env TEST_COMMENT="watch expression generate detect event" ${lnav_test} -n \
|
||||
-c ';SELECT json(content) as content FROM lnav_events' \
|
||||
-c ':write-jsonlines-to -' \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
run_cap_test env TEST_COMMENT="show the configuration" ${lnav_test} -nN \
|
||||
-c ':config /log/watch-expressions'
|
||||
|
||||
run_cap_test env TEST_COMMENT="delete the configuration" ${lnav_test} -nN \
|
||||
-c ':reset-config /log/watch-expressions/http-errors/'
|
||||
|
||||
run_cap_test env TEST_COMMENT="config should be gone now" ${lnav_test} -nN \
|
||||
-c ':config /log/watch-expressions'
|
||||
|
Loading…
Reference in New Issue