include/SparseSetAbstractDomain.h (170 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <iterator> #include <ostream> #include <vector> #include "PowersetAbstractDomain.h" namespace sparta { namespace ssad_impl { /* * An implementation of a powerset abstract domain based on the sparse data * structure described in the following paper: * * P. Briggs & L. Torczon. An Efficient Representation for Sparse Sets. ACM * Letters on Programming Languages and Systems, 2(1-4):59-69,1993. * * This powerset domain can only handle elements that are unsigned integers * belonging to a fixed-size universe {0, ..., max_size-1}. */ template <typename IntegerType> class SparseSetValue final : public PowersetImplementation<IntegerType, std::vector<IntegerType>, SparseSetValue<IntegerType>> { public: using iterator = typename std::vector<IntegerType>::iterator; using const_iterator = typename std::vector<IntegerType>::const_iterator; // This constructor is defined solely to satisfy the requirement that an // AbstractDomain must be default-constructible. It shouldn't be used in // practice. SparseSetValue() : m_capacity(0), m_element_num(0) {} // Returns an empty set over a universe of the given size. SparseSetValue(size_t max_size) : m_capacity(max_size), m_element_num(0), m_dense(max_size), m_sparse(max_size) {} void clear() override { m_element_num = 0; } // Returns a vector that contains all the elements in the sparse set. std::vector<IntegerType> elements() const override { return std::vector<IntegerType>(begin(), end()); } AbstractValueKind kind() const override { return AbstractValueKind::Value; } bool contains(const IntegerType& element) const override { if (element >= m_capacity) { return false; } size_t dense_idx = m_sparse[element]; return dense_idx < m_element_num && m_dense[dense_idx] == element; } bool leq(const SparseSetValue& other) const override { if (m_element_num > other.m_element_num) { return false; } for (size_t i = 0; i < m_element_num; ++i) { if (!other.contains(m_dense[i])) { return false; } } return true; } bool equals(const SparseSetValue& other) const override { return (m_element_num == other.m_element_num) && this->leq(other); } void add(const IntegerType& element) override { if (element < m_capacity) { size_t dense_idx = m_sparse[element]; size_t n = m_element_num; if (dense_idx >= m_element_num || m_dense[dense_idx] != element) { m_sparse[element] = n; m_dense[n] = element; m_element_num = n + 1; } } } void remove(const IntegerType& element) override { if (element < m_capacity) { size_t dense_idx = m_sparse[element]; size_t n = m_element_num; if (dense_idx < n && m_dense[dense_idx] == element) { IntegerType last_element = m_dense[n - 1]; m_element_num = n - 1; m_dense[dense_idx] = last_element; m_sparse[last_element] = dense_idx; } } } iterator begin() { return m_dense.begin(); } iterator end() { return std::next(m_dense.begin(), m_element_num); } const_iterator begin() const { return m_dense.begin(); } const_iterator end() const { return std::next(m_dense.begin(), m_element_num); } AbstractValueKind join_with(const SparseSetValue& other) override { if (other.m_capacity > m_capacity) { m_dense.resize(other.m_capacity); m_sparse.resize(other.m_capacity); m_capacity = other.m_capacity; } for (IntegerType e : other) { add(e); } return AbstractValueKind::Value; } AbstractValueKind widen_with(const SparseSetValue& other) override { return join_with(other); } AbstractValueKind meet_with(const SparseSetValue& other) override { for (auto it = begin(); it != end();) { if (!other.contains(*it)) { // If other doesn't contain this element, we remove it using the current // position. The function remove() will fill this position with the last // element in the dense array. remove(*it); } else { // If other contains this element, we just move on to the next position. ++it; } } return AbstractValueKind::Value; } AbstractValueKind narrow_with(const SparseSetValue& other) override { return meet_with(other); } AbstractValueKind difference_with(const SparseSetValue& other) override { for (auto it = begin(); it != end();) { if (other.contains(*it)) { remove(*it); } else { ++it; } } return AbstractValueKind::Value; } size_t size() const override { return m_element_num; } friend std::ostream& operator<<(std::ostream& o, const SparseSetValue& value) { o << "[#" << value.size() << "]"; const auto& elements = value.elements(); o << "{"; for (auto it = elements.begin(); it != elements.end();) { o << *it++; if (it != elements.end()) { o << ", "; } } o << "}"; return o; } private: size_t m_capacity; size_t m_element_num; std::vector<IntegerType> m_dense; std::vector<size_t> m_sparse; }; } // namespace ssad_impl /* * We defined a powerset abstract domain based on the sparse set data structure. */ template <typename IntegerType> class SparseSetAbstractDomain final : public PowersetAbstractDomain<IntegerType, ssad_impl::SparseSetValue<IntegerType>, std::vector<IntegerType>, SparseSetAbstractDomain<IntegerType>> { public: using Value = ssad_impl::SparseSetValue<IntegerType>; ~SparseSetAbstractDomain() { // The destructor is the only method that is guaranteed to be created when // a class template is instantiated. This is a good place to perform all // the sanity checks on the template parameters. static_assert(std::is_unsigned<IntegerType>::value, "IntegerType is not an unsigned arihmetic type"); static_assert(sizeof(IntegerType) <= sizeof(size_t), "IntegerType is too large"); } SparseSetAbstractDomain() : PowersetAbstractDomain<IntegerType, Value, std::vector<IntegerType>, SparseSetAbstractDomain>() {} explicit SparseSetAbstractDomain(AbstractValueKind kind) : PowersetAbstractDomain<IntegerType, Value, std::vector<IntegerType>, SparseSetAbstractDomain>(kind) {} explicit SparseSetAbstractDomain(IntegerType max_size) { this->set_to_value(Value(max_size)); } static SparseSetAbstractDomain bottom() { return SparseSetAbstractDomain(AbstractValueKind::Bottom); } static SparseSetAbstractDomain top() { return SparseSetAbstractDomain(AbstractValueKind::Top); } }; } // namespace sparta