commit 96249c0adef7f4f54630398c00b90cbeb7291a78
parent 1556f65ddc63e3da13bfc340a6bf3dbbb3512a4f
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date: Tue, 1 Nov 2022 15:26:55 +0000
Runtime checks
Diffstat:
10 files changed, 167 insertions(+), 48 deletions(-)
diff --git a/include/kfr/base/expression.hpp b/include/kfr/base/expression.hpp
@@ -399,13 +399,12 @@ struct expression_function : expression_with_arguments<Args...>, expression_trai
constexpr static shape<dims> shapeof(const expression_function& self)
{
return self.fold([&](auto&&... args) CMT_INLINE_LAMBDA constexpr->auto {
- return internal_generic::common_shape(expression_traits<decltype(args)>::shapeof(args)...);
+ return internal_generic::common_shape<true>(expression_traits<decltype(args)>::shapeof(args)...);
});
}
constexpr static shape<dims> shapeof()
{
- return expression_function<Fn,
- Args...>::fold_idx([&](auto... args) CMT_INLINE_LAMBDA constexpr->auto {
+ return expression_function::fold_idx([&](auto... args) CMT_INLINE_LAMBDA constexpr->auto {
return internal_generic::common_shape(
expression_traits<
typename expression_function::template nth<val_of(decltype(args)())>>::shapeof()...);
@@ -419,28 +418,14 @@ struct expression_function : expression_with_arguments<Args...>, expression_trai
KFR_MEM_INTRINSIC expression_function(expression_with_arguments<Args...> args, Fn&& fn)
: expression_with_arguments<Args...>{ std::move(args) }, fn(std::forward<Fn>(fn))
{
- check_shapes();
}
KFR_MEM_INTRINSIC expression_function(Fn&& fn, Args&&... args)
: expression_with_arguments<Args...>{ std::forward<Args>(args)... }, fn(std::forward<Fn>(fn))
{
- check_shapes();
}
KFR_MEM_INTRINSIC expression_function(Args&&... args)
: expression_with_arguments<Args...>{ std::forward<Args>(args)... }, fn{}
{
- check_shapes();
- }
- KFR_MEM_INTRINSIC void check_shapes()
- {
- if constexpr (dims > 0)
- {
- shape<dims> sh = shapeof(*this);
- if (sh == shape<dims>(0))
- {
- // throw std::runtime_error("KFR: Invalid shapes in expression_function");
- }
- }
}
template <typename In, enable_if_input_expression<In>* = nullptr>
@@ -578,10 +563,9 @@ KFR_INTRINSIC static void tprocess_body(Out&& out, In&& in, size_t start, size_t
{
outidx[OutAxis] = x;
inidx[InAxis] = std::min(x, insize - 1);
- auto v = get_elements(in, inidx, axis_params_v<InAxis, w>);
+ auto v = get_elements(in, inidx, axis_params_v<InAxis, w>);
// println("## i=", x, "\n", v);
- set_elements(out, outidx, axis_params_v<OutAxis, w>,
- v );
+ set_elements(out, outidx, axis_params_v<OutAxis, w>, v);
}
}
CMT_LOOP_NOUNROLL
diff --git a/include/kfr/base/shape.hpp b/include/kfr/base/shape.hpp
@@ -25,6 +25,7 @@
*/
#pragma once
+#include "../except.hpp"
#include "impl/static_array.hpp"
#include "../cometa/string.hpp"
@@ -146,7 +147,7 @@ struct shape : static_array_base<index_t, csizeseq_t<dims>>
{
for (index_t i = 0; i < dims; ++i)
{
- if (this->operator[](i) == infinite_size)
+ if (CMT_UNLIKELY(this->operator[](i) == infinite_size))
return true;
}
return false;
@@ -317,7 +318,7 @@ struct shape : static_array_base<index_t, csizeseq_t<dims>>
}
KFR_MEM_INTRINSIC constexpr void set_revindex(size_t index, index_t val)
{
- if (index < dims)
+ if (CMT_LIKELY(index < dims))
this->operator[](dims - 1 - index) = val;
}
};
@@ -469,7 +470,7 @@ constexpr KFR_INTRINSIC shape<outdims> compact_shape(const shape<dims>& in)
size_t j = 0;
for (size_t i = 0; i < dims; ++i)
{
- if (i >= flags.size() || flags[i])
+ if (CMT_LIKELY(i >= flags.size() || flags[i]))
{
result[j++] = in[i];
}
@@ -500,8 +501,8 @@ bool can_assign_from(const shape<dims1>& dst_shape, const shape<dims2>& src_shap
{
index_t dst_size = dst_shape.revindex(i);
index_t src_size = src_shape.revindex(i);
- if (src_size == 1 || src_size == infinite_size || src_size == dst_size ||
- dst_size == infinite_size)
+ if (CMT_LIKELY(src_size == 1 || src_size == infinite_size || src_size == dst_size ||
+ dst_size == infinite_size))
{
}
else
@@ -514,13 +515,13 @@ bool can_assign_from(const shape<dims1>& dst_shape, const shape<dims2>& src_shap
}
}
-template <index_t dims>
+template <bool checked = false, index_t dims>
constexpr shape<dims> common_shape(const shape<dims>& shape)
{
return shape;
}
-template <index_t dims1, index_t dims2, index_t outdims = const_max(dims1, dims2)>
+template <bool checked = false, index_t dims1, index_t dims2, index_t outdims = const_max(dims1, dims2)>
KFR_MEM_INTRINSIC constexpr shape<outdims> common_shape(const shape<dims1>& shape1,
const shape<dims2>& shape2)
{
@@ -529,15 +530,15 @@ KFR_MEM_INTRINSIC constexpr shape<outdims> common_shape(const shape<dims1>& shap
{
index_t size1 = shape1.revindex(i);
index_t size2 = shape2.revindex(i);
- if (!size1 || !size2)
+ if (CMT_UNLIKELY(!size1 || !size2))
{
result[outdims - 1 - i] = 0;
continue;
}
- if (size1 == infinite_size)
+ if (CMT_UNLIKELY(size1 == infinite_size))
{
- if (size2 == infinite_size)
+ if (CMT_UNLIKELY(size2 == infinite_size))
{
result[outdims - 1 - i] = infinite_size;
}
@@ -548,21 +549,28 @@ KFR_MEM_INTRINSIC constexpr shape<outdims> common_shape(const shape<dims1>& shap
}
else
{
- if (size2 == infinite_size)
+ if (CMT_UNLIKELY(size2 == infinite_size))
{
result[outdims - 1 - i] = size1 == 1 ? infinite_size : size1;
}
else
{
- if (size1 == 1 || size2 == 1 || size1 == size2)
+ if (CMT_LIKELY(size1 == 1 || size2 == 1 || size1 == size2))
{
result[outdims - 1 - i] = std::max(size1, size2);
}
else
{
// broadcast failed
- result = shape<outdims>(0);
- return result;
+ if constexpr (checked)
+ {
+ KFR_LOGIC_CHECK(false, "invalid or incompatible shapes: ", shape1, " and ", shape2);
+ }
+ else
+ {
+ result = shape<outdims>(0);
+ return result;
+ }
}
}
}
@@ -570,18 +578,19 @@ KFR_MEM_INTRINSIC constexpr shape<outdims> common_shape(const shape<dims1>& shap
return result;
}
-template <>
+template <bool checked = false>
KFR_MEM_INTRINSIC constexpr shape<0> common_shape(const shape<0>& shape1, const shape<0>& shape2)
{
return {};
}
-template <index_t dims1, index_t dims2, index_t... dims, index_t outdims = const_max(dims1, dims2, dims...)>
+template <bool checked = false, index_t dims1, index_t dims2, index_t... dims,
+ index_t outdims = const_max(dims1, dims2, dims...)>
KFR_MEM_INTRINSIC constexpr shape<outdims> common_shape(const shape<dims1>& shape1,
const shape<dims2>& shape2,
const shape<dims>&... shapes)
{
- return common_shape(shape1, common_shape(shape2, shapes...));
+ return common_shape<checked>(shape1, common_shape(shape2, shapes...));
}
template <index_t dims1, index_t dims2>
@@ -691,7 +700,7 @@ KFR_INTRINSIC shape<dims> increment_indices_return(const shape<dims>& indices, c
const shape<dims>& stop, index_t dim = dims - 1)
{
shape<dims> result = indices;
- if (increment_indices(result, start, stop, dim))
+ if (CMT_LIKELY(increment_indices(result, start, stop, dim)))
{
return result;
}
diff --git a/include/kfr/cident.h b/include/kfr/cident.h
@@ -457,13 +457,15 @@ extern char* gets(char* __s);
#define CMT_HAS_BUILTIN(builtin) 0
#endif
-#if CMT_HAS_BUILTIN(CMT_ASSUME)
-#define CMT_ASSUME(x) __builtin_assume(x)
-#else
-#define CMT_ASSUME(x) \
+#define CMT_NOOP \
do \
{ \
} while (0)
+
+#if CMT_HAS_BUILTIN(CMT_ASSUME)
+#define CMT_ASSUME(x) __builtin_assume(x)
+#else
+#define CMT_ASSUME(x) CMT_NOOP
#endif
#if CMT_HAS_BUILTIN(CMT_ASSUME)
diff --git a/include/kfr/dsp/ebu.hpp b/include/kfr/dsp/ebu.hpp
@@ -264,6 +264,10 @@ public:
: m_sample_rate(sample_rate), m_running(true), m_need_reset(false),
m_packet_size(sample_rate / 10 / packet_size_factor)
{
+ KFR_LOGIC_CHECK(!channels.empty(), "channels must not be empty");
+ KFR_LOGIC_CHECK(sample_rate > 0, "sample_rate must be greater than 0");
+ KFR_LOGIC_CHECK(packet_size_factor >= 1 && packet_size_factor <= 3,
+ "packet_size_factor must be in range [1..3]");
for (Speaker sp : channels)
{
m_channels.emplace_back(sample_rate, sp, packet_size_factor, T(1));
@@ -311,7 +315,6 @@ public:
T shortterm = 0;
for (size_t ch = 0; ch < m_channels.size(); ch++)
{
- // println(ch, "=> ", source[ch][0], " ", source[ch][10], " ", source[ch][20] );
TESTO_ASSERT(source[ch].size() == m_packet_size);
ebu_channel<T>& chan = m_channels[ch];
chan.process_packet(source[ch].data());
diff --git a/include/kfr/except.hpp b/include/kfr/except.hpp
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2016-2022 Fractalium Ltd (https://www.kfrlib.com)
+ This file is part of KFR
+
+ KFR is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ KFR is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with KFR.
+
+ If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
+ Buying a commercial license is mandatory as soon as you develop commercial activities without
+ disclosing the source code of your own applications.
+ See https://www.kfrlib.com for details.
+ */
+#pragma once
+#include "cident.h"
+#include "cometa/string.hpp"
+#include <exception>
+
+namespace kfr
+{
+
+class exception : public std::exception
+{
+public:
+ using std::exception::exception;
+ exception(const std::string& str) : exception(str.c_str()) {}
+};
+class logic_error : public exception
+{
+public:
+ using exception::exception;
+};
+class runtime_error : public exception
+{
+public:
+ using exception::exception;
+};
+
+#ifndef KFR_THROW_EXCEPTION
+#define KFR_THROW_EXCEPTION(kind, ...) \
+ do \
+ { \
+ throw ::kfr::CMT_CONCAT(kind, _error)(kfr::as_string(__VA_ARGS__)); \
+ } while (0)
+#endif
+
+#define KFR_PRINT_AND_ABORT(kind, ...) \
+ do \
+ { \
+ std::string s = kfr::as_string(__VA_ARGS__); \
+ std::fprintf(stderr, "KFR " CMT_STRINGIFY(kind) " error: %s\n", s.c_str()); \
+ std::fflush(stderr); \
+ std::abort(); \
+ } while (0)
+
+#if defined __cpp_exceptions || defined _HAS_EXCEPTIONS || defined _EXCEPTIONS
+#define KFR_REPORT_ERROR KFR_THROW_EXCEPTION
+#else
+#define KFR_REPORT_ERROR KFR_PRINT_AND_ABORT
+#endif
+
+#define KFR_CHECK_IMPL(cond, kind, ...) \
+ do \
+ { \
+ if (CMT_UNLIKELY(!(cond))) \
+ KFR_REPORT_ERROR(kind, __VA_ARGS__); \
+ } while (0)
+
+#if !defined(KFR_DISABLE_CHECKS)
+
+#define KFR_RUNTIME_CHECK(cond, ...) KFR_CHECK_IMPL(cond, runtime, __VA_ARGS__)
+
+#define KFR_LOGIC_CHECK(cond, ...) KFR_CHECK_IMPL(cond, logic, __VA_ARGS__)
+
+#else
+#define KFR_RUNTIME_CHECK(cond, ...) CMT_NOOP
+#define KFR_LOGIC_CHECK(cond, ...) CMT_NOOP
+
+#endif
+
+} // namespace kfr
diff --git a/include/kfr/kfr.h b/include/kfr/kfr.h
@@ -1,3 +1,25 @@
+/*
+ Copyright (C) 2016-2022 Fractalium Ltd (https://www.kfrlib.com)
+ This file is part of KFR
+
+ KFR is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ KFR is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with KFR.
+
+ If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
+ Buying a commercial license is mandatory as soon as you develop commercial activities without
+ disclosing the source code of your own applications.
+ See https://www.kfrlib.com for details.
+ */
/** @addtogroup utility
* @{
*/
diff --git a/include/kfr/simd/impl/backend_generic.hpp b/include/kfr/simd/impl/backend_generic.hpp
@@ -71,10 +71,7 @@ struct shuffle_mask<2, i0, i1>
#if KFR_SHOW_NOT_OPTIMIZED
CMT_PUBLIC_C CMT_DLL_EXPORT void not_optimized(const char* fn) CMT_NOEXCEPT;
#else
-#define not_optimized(...) \
- do \
- { \
- } while (0)
+#define not_optimized(...) CMT_NOOP
#endif
inline namespace CMT_ARCH_NAME
diff --git a/sources.cmake b/sources.cmake
@@ -9,6 +9,7 @@ set(
${PROJECT_SOURCE_DIR}/include/kfr/cometa.hpp
${PROJECT_SOURCE_DIR}/include/kfr/dft.hpp
${PROJECT_SOURCE_DIR}/include/kfr/dsp.hpp
+ ${PROJECT_SOURCE_DIR}/include/kfr/except.hpp
${PROJECT_SOURCE_DIR}/include/kfr/graphics.hpp
${PROJECT_SOURCE_DIR}/include/kfr/io.hpp
${PROJECT_SOURCE_DIR}/include/kfr/math.hpp
@@ -317,6 +318,7 @@ set(
${PROJECT_SOURCE_DIR}/include/kfr/cometa.hpp
${PROJECT_SOURCE_DIR}/include/kfr/dft.hpp
${PROJECT_SOURCE_DIR}/include/kfr/dsp.hpp
+ ${PROJECT_SOURCE_DIR}/include/kfr/except.hpp
${PROJECT_SOURCE_DIR}/include/kfr/graphics.hpp
${PROJECT_SOURCE_DIR}/include/kfr/io.hpp
${PROJECT_SOURCE_DIR}/include/kfr/math.hpp
diff --git a/tests/unit/base/basic_expressions.cpp b/tests/unit/base/basic_expressions.cpp
@@ -69,6 +69,12 @@ TEST(padded)
[](size_t i) { return i >= 501 ? -1 : i; });
}
+TEST(concatenate)
+{
+ CHECK_EXPRESSION(concatenate(truncate(counter(5, 0), 5), truncate(counter(10, 0), 5)),
+ { 5, 5, 5, 5, 5, 10, 10, 10, 10, 10 });
+}
+
TEST(rebind)
{
auto c_minus_two = counter() - 2;
@@ -119,7 +125,7 @@ TEST(assign_expression)
univector<float> f = truncate(counter(0, 1), 10);
f *= 10;
CHECK_EXPRESSION(f, { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 });
-
+
univector<float> a = truncate(counter(0, 1), 10);
univector<float> b = truncate(counter(100, 1), 10);
pack(a, b) *= broadcast<2>(10.f);
diff --git a/tests/unit/base/shape.cpp b/tests/unit/base/shape.cpp
@@ -52,6 +52,10 @@ TEST(shape_broadcast)
CHECK(common_shape(shape{}, shape{ 0 }) == shape{ 0 });
CHECK(common_shape(shape{}, shape{ 0, 0 }) == shape{ 0, 0 });
CHECK(common_shape(shape{ 0 }, shape{ 0, 0 }) == shape{ 0, 0 });
+
+ CHECK(common_shape<true>(shape{}, shape{ 0 }) == shape{ 0 });
+ CHECK(common_shape<true>(shape{}, shape{ 0, 0 }) == shape{ 0, 0 });
+ CHECK(common_shape<true>(shape{ 0 }, shape{ 0, 0 }) == shape{ 0, 0 });
CHECK(can_assign_from(shape{ 1, 4 }, shape{ 1, 4 }));
CHECK(!can_assign_from(shape{ 1, 4 }, shape{ 4, 1 }));