Skip to content

Commit

Permalink
Contracts header with assertion macros specific to Sparrow (#77)
Browse files Browse the repository at this point in the history
* Contracts header with assertion macros specific to Sparrow

This provides macros (extracted from my library NoContracts) for contract assertions checks,
named with SPARROW_ so that control over enabling these assertions is let to the user.
The checks are enabled by default and all C `assert(...)` calls have been replaced.

* Use SPARROW_ASSERT_FALSE where it makes sense
  • Loading branch information
Klaim authored Apr 19, 2024
1 parent a1e1b95 commit bef873a
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 50 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ set(SPARROW_HEADERS
${SPARROW_INCLUDE_DIR}/sparrow/array_data.hpp
${SPARROW_INCLUDE_DIR}/sparrow/buffer.hpp
${SPARROW_INCLUDE_DIR}/sparrow/buffer_view.hpp
${SPARROW_INCLUDE_DIR}/sparrow/contracts.hpp
${SPARROW_INCLUDE_DIR}/sparrow/data_type.hpp
${SPARROW_INCLUDE_DIR}/sparrow/data_traits.hpp
${SPARROW_INCLUDE_DIR}/sparrow/dynamic_bitset.hpp
Expand Down
8 changes: 4 additions & 4 deletions include/sparrow/array_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

#pragma once

#include <cassert>
#include <compare>
#include <optional>
#include <vector>

#include "sparrow/contracts.hpp"
#include "sparrow/buffer.hpp"
#include "sparrow/data_type.hpp"
#include "sparrow/dynamic_bitset.hpp"
Expand Down Expand Up @@ -327,7 +327,7 @@ namespace sparrow
template <class L>
auto const_reference_proxy<L>::value() const -> const_reference
{
assert(has_value());
SPARROW_ASSERT_TRUE(has_value());
return m_val_ref;
}

Expand Down Expand Up @@ -357,14 +357,14 @@ namespace sparrow
template <class L>
auto reference_proxy<L>::value() -> value_type&
{
assert(has_value());
SPARROW_ASSERT_TRUE(has_value());
return m_val_ref;
}

template <class L>
auto reference_proxy<L>::value() const -> const value_type&
{
assert(has_value());
SPARROW_ASSERT_TRUE(has_value());
return m_val_ref;
}

Expand Down
10 changes: 8 additions & 2 deletions include/sparrow/buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
#pragma once

#include <algorithm>
#include <cassert>
#include <concepts>
#include <cstdint>
#include <iterator>
#include <stdexcept>

#include "sparrow/contracts.hpp"
#include "sparrow/allocator.hpp"
#include "sparrow/iterator.hpp"
#include "sparrow/mp_utils.hpp"
Expand Down Expand Up @@ -370,7 +370,7 @@ namespace sparrow
template <class T>
constexpr void buffer_base<T>::assign_storage(pointer p, size_type n, size_type cap)
{
assert(n <= cap);
SPARROW_ASSERT_TRUE(n <= cap);
m_data.p_begin = p;
m_data.p_end = p + n;
m_data.p_storage_end = p + cap;
Expand Down Expand Up @@ -501,36 +501,42 @@ namespace sparrow
template <class T>
constexpr auto buffer<T>::operator[](size_type i) -> reference
{
SPARROW_ASSERT_TRUE(i < size());
return get_data().p_begin[i];
}

template <class T>
constexpr auto buffer<T>::operator[](size_type i) const -> const_reference
{
SPARROW_ASSERT_TRUE(i < size());
return get_data().p_begin[i];
}

template <class T>
constexpr auto buffer<T>::front() -> reference
{
SPARROW_ASSERT_FALSE(empty());
return *(get_data().p_begin);
}

template <class T>
constexpr auto buffer<T>::front() const -> const_reference
{
SPARROW_ASSERT_FALSE(empty());
return *(get_data().p_begin);
}

template <class T>
constexpr auto buffer<T>::back() -> reference
{
SPARROW_ASSERT_FALSE(empty());
return *(get_data().p_end - 1);
}

template <class T>
constexpr auto buffer<T>::back() const -> const_reference
{
SPARROW_ASSERT_FALSE(empty());
return *(get_data().p_end - 1);
}

Expand Down
144 changes: 144 additions & 0 deletions include/sparrow/contracts.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2024 Man Group Operations Limited

Check notice on line 1 in include/sparrow/contracts.hpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on include/sparrow/contracts.hpp

File include/sparrow/contracts.hpp does not conform to Custom style guidelines. (lines 15, 16, 19, 28, 29, 30, 31, 32, 33, 36, 37, 38, 43, 44, 45, 46, 47, 48, 50, 51, 52, 57, 58, 59, 60, 61, 62, 66, 67, 69, 70, 71, 72, 74, 75, 76, 77, 79, 80, 81, 85, 91, 92, 96, 97, 98, 99, 100, 109, 110, 111, 119, 122, 124, 125, 126, 127, 129, 130, 131, 132, 133, 135, 136, 138, 139, 140, 141, 142, 143)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// NOTE: This is based upon v0.3.0 of NoContracts library https://github.com/Klaim/nocontracts/tree/0ffc183f7527213e3f0a57b8dae9befa7c0ca02c
// Modifications: renamed macros

#pragma once
#include <cstdio>
#include <csignal>


///////////////////////////////////////////////////////////////////
// Possible bits used to compose the behavior:

// if not specified by the user but available, use std::print
#if not defined(SPARROW_CONTRACTS_USE_STD_PRINT) and not defined(SPARROW_CONTRACTS_USE_STD_FORMAT)
# if __cplusplus >= 202002L
# include <version>
# ifdef __cpp_lib_print
# define SPARROW_CONTRACTS_USE_STD_PRINT 1
# endif
# endif
#else
// The option is defined, but is it supported? If not we fail.
# if defined(SPARROW_CONTRACTS_USE_STD_PRINT) and not defined(__cpp_lib_print)
# error "std::print usage is requested but not available"
# endif
#endif

// if not specified by the user but available and std::print is not already forced, use std::format
#if not defined(SPARROW_CONTRACTS_USE_STD_FORMAT) and not defined(SPARROW_CONTRACTS_USE_STD_PRINT)
# if __cplusplus >= 202002L
# include <version>
# ifdef __cpp_lib_format
# define SPARROW_CONTRACTS_USE_STD_FORMAT 1
# endif
# endif
// The option is defined, but is it supported? If not we fail.
# if defined(SPARROW_CONTRACTS_USE_STD_FORMAT) and not defined(__cpp_lib_format)
# error "std::format usage is requested but not available"
# endif
#endif

// user specified to use neither std::format nor std::print, but C's formatting
#if defined(SPARROW_CONTRACTS_USE_CFORMAT) && SPARROW_CONTRACTS_USE_CFORMAT == 1
# ifdef SPARROW_CONTRACTS_USE_STD_FORMAT
# undef SPARROW_CONTRACTS_USE_STD_FORMAT
# endif
# ifdef SPARROW_CONTRACTS_USE_STD_PRINT
# undef SPARROW_CONTRACTS_USE_STD_PRINT
# endif
#endif

#ifndef SPARROW_CONTRACTS_LOG_FAILURE
# if defined(SPARROW_CONTRACTS_USE_STD_PRINT) && SPARROW_CONTRACTS_USE_STD_PRINT == 1
# include <print>
# include <cstdio>
# define SPARROW_CONTRACTS_LOG_FAILURE( expr__, message__ ) \
::std::print(stderr, "Assertion Failed ({}:{}): {} - ({} is wrong)\n", __FILE__, __LINE__, message__, #expr__ )
# elif defined(SPARROW_CONTRACTS_USE_STD_FORMAT) && SPARROW_CONTRACTS_USE_STD_FORMAT == 1
# include <format>
# include <cstdio>
# define SPARROW_CONTRACTS_LOG_FAILURE( expr__, message__ ) \
::fprintf(stderr, ::std::format("Assertion Failed ({}:{}): {} - ({} is wrong)\n", __FILE__, __LINE__, message__, #expr__ ).c_str())
# else
# include <cstdlib>
# include <cstdio>
# define SPARROW_CONTRACTS_LOG_FAILURE( expr__, message__ ) \
::fprintf(stderr, "Assertion Failed (%s:%i): %s - (%s is wrong)\n", __FILE__, __LINE__, message__, #expr__ )
# endif
#endif

#ifndef SPARROW_CONTRACTS_ABORT
# define SPARROW_CONTRACTS_ABORT() \
std::abort()
#endif

// User specifies to just continue instead of abort on failure.
#if defined(SPARROW_CONTRACTS_CONTINUE_ON_FAILURE) and SPARROW_CONTRACTS_CONTINUE_ON_FAILURE == 1
# undef SPARROW_CONTRACTS_ABORT
# define SPARROW_CONTRACTS_ABORT
#endif

#ifndef SPARROW_CONTRACTS_DEBUGBREAK
# ifdef _WIN32
# define SPARROW_CONTRACTS_DEBUGBREAK() __debugbreak();
# else
# define SPARROW_CONTRACTS_DEBUGBREAK() std::raise(SIGTRAP);
# endif
#endif

///////////////////////////////////////////////////////////////////
// Default behavior:

#define SPARROW_CONTRACTS_DEFAULT_CHECKS_ENABLED 1
#define SPARROW_CONTRACTS_DEFAULT_ABORT_ON_FAILURE 1

#define SPARROW_CONTRACTS_DEFAULT_ON_FAILURE( expr__, message__ ) \
SPARROW_CONTRACTS_LOG_FAILURE( expr__, message__ ); \
SPARROW_CONTRACTS_DEBUGBREAK(); \
SPARROW_CONTRACTS_ABORT();

///////////////////////////////////////////////////////////////////
// Apply Configuration:

// If not override, use the default behavior.
#ifndef SPARROW_CONTRACTS_CHECKS_ENABLED
# define SPARROW_CONTRACTS_CHECKS_ENABLED SPARROW_CONTRACTS_DEFAULT_CHECKS_ENABLED
#endif

#if SPARROW_CONTRACTS_CHECKS_ENABLED == 1 // Behavior when contracts are enabled.

# ifndef SPARROW_CONTRACTS_ON_FAILURE
# define SPARROW_CONTRACTS_ON_FAILURE( expr__, message__ ) \
SPARROW_CONTRACTS_DEFAULT_ON_FAILURE( expr__, message__ )
# endif

# ifndef SPARROW_ASSERT
# define SPARROW_ASSERT( expr__, message__ ) \
if(!( expr__ )) \
{ SPARROW_CONTRACTS_ON_FAILURE( expr__, message__ ); }
# endif

# define SPARROW_ASSERT_TRUE( expr__ ) SPARROW_ASSERT( expr__, #expr__ )
# define SPARROW_ASSERT_FALSE( expr__ ) SPARROW_ASSERT( !(expr__), "!("#expr__")" )

# else // Do nothing otherwise.
# define SPARROW_CONTRACTS_ON_FAILURE( expr__ )
# define SPARROW_ASSERT( expr__, message__ )
# define SPARROW_ASSERT_TRUE( expr__ )
# define SPARROW_ASSERT_FALSE( expr__ )
#endif

7 changes: 4 additions & 3 deletions include/sparrow/dictionary_encoded_layout.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#pragma once

#include "sparrow/contracts.hpp"
#include "sparrow/array_data.hpp"
#include "sparrow/fixed_size_layout.hpp"
#include "sparrow/iterator.hpp"
Expand Down Expand Up @@ -203,7 +204,7 @@ namespace sparrow
template <class IL, class SL, bool is_const>
auto dictionary_value_iterator<IL, SL, is_const>::dereference() const -> reference
{
assert(m_sub_layout_reference.has_value());
SPARROW_ASSERT_TRUE(m_sub_layout_reference.has_value());
return (*m_sub_layout_reference).get()[*m_index_it];
}

Expand Down Expand Up @@ -250,7 +251,7 @@ namespace sparrow
template <std::integral T, class SL, layout_offset OT>
dictionary_encoded_layout<T, SL, OT>::dictionary_encoded_layout(array_data& data)
{
assert(data.dictionary);
SPARROW_ASSERT_TRUE(data.dictionary);
m_sub_layout = std::make_unique<SL>(*data.dictionary);
m_indexes_layout = std::make_unique<indexes_layout>(data);
}
Expand All @@ -264,7 +265,7 @@ namespace sparrow
template <std::integral T, class SL, layout_offset OT>
auto dictionary_encoded_layout<T, SL, OT>::operator[](size_type i) const -> const_reference
{
assert(i < size());
SPARROW_ASSERT_TRUE(i < size());
const auto index = (*m_indexes_layout)[i];
if (index.has_value())
{
Expand Down
20 changes: 10 additions & 10 deletions include/sparrow/dynamic_bitset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

#pragma once

#include <cassert>
#include <climits>
#include <concepts>
#include <ranges>

#include "sparrow/contracts.hpp"
#include "sparrow/buffer.hpp"
#include "sparrow/buffer_view.hpp"
#include "sparrow/mp_utils.hpp"
Expand Down Expand Up @@ -314,28 +314,28 @@ namespace sparrow
template <random_access_range B>
auto dynamic_bitset_base<B>::operator[](size_type pos) -> reference
{
assert(pos < size());
SPARROW_ASSERT_TRUE(pos < size());
return reference(*this, m_buffer.data()[block_index(pos)], bit_mask(pos));
}

template <random_access_range B>
bool dynamic_bitset_base<B>::operator[](size_type pos) const
{
assert(pos < size());
SPARROW_ASSERT_TRUE(pos < size());
return !m_null_count || m_buffer.data()[block_index(pos)] & bit_mask(pos);
}

template <random_access_range B>
bool dynamic_bitset_base<B>::test(size_type pos) const
{
assert(pos < size());
SPARROW_ASSERT_TRUE(pos < size());
return !m_null_count || m_buffer.data()[block_index(pos)] & bit_mask(pos);
}

template <random_access_range B>
void dynamic_bitset_base<B>::set(size_type pos, value_type value)
{
assert(pos < size());
SPARROW_ASSERT_TRUE(pos < size());
block_type& block = m_buffer.data()[block_index(pos)];
const bool old_value = block & bit_mask(pos);
if (value)
Expand Down Expand Up @@ -430,7 +430,7 @@ namespace sparrow
, m_null_count(null_count)
{
zero_unused_bits();
assert(m_null_count == m_size - count_non_null());
SPARROW_ASSERT_TRUE(m_null_count == m_size - count_non_null());
}

template <random_access_range B>
Expand Down Expand Up @@ -698,7 +698,7 @@ namespace sparrow
, p_block(block)
, m_index(index)
{
assert(m_index < bitset_type::s_bits_per_block);
SPARROW_ASSERT_TRUE(m_index < bitset_type::s_bits_per_block);
}

template <class B, bool is_const>
Expand All @@ -724,7 +724,7 @@ namespace sparrow
++p_block;
m_index = 0u;
}
assert(m_index < bitset_type::s_bits_per_block);
SPARROW_ASSERT_TRUE(m_index < bitset_type::s_bits_per_block);
}

template <class B, bool is_const>
Expand All @@ -740,7 +740,7 @@ namespace sparrow
{
--m_index;
}
assert(m_index < bitset_type::s_bits_per_block);
SPARROW_ASSERT_TRUE(m_index < bitset_type::s_bits_per_block);
}

template <class B, bool is_const>
Expand Down Expand Up @@ -786,7 +786,7 @@ namespace sparrow
}
}
}
assert(m_index < bitset_type::s_bits_per_block);
SPARROW_ASSERT_TRUE(m_index < bitset_type::s_bits_per_block);
}

template <class B, bool is_const>
Expand Down
Loading

0 comments on commit bef873a

Please sign in to comment.