<commit_before>
/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>
#include <memory>
#include "bytes.hh"
#include "utils/allocation_strategy.hh"
#include <seastar/core/unaligned.hh>
#include <seastar/util/alloc_failure_injector.hh>
#include <unordered_map>
#include <type_traits>

struct blob_storage {
    struct [[gnu::packed]] ref_type {
        blob_storage* ptr;

        ref_type() {}
        ref_type(blob_storage* ptr) : ptr(ptr) {}
        operator blob_storage*() const { return ptr; }
        blob_storage* operator->() const { return ptr; }
        blob_storage& operator*() const { return *ptr; }
    };
    using size_type = uint32_t;
    using char_type = bytes_view::value_type;

    ref_type* backref;
    size_type size;
    size_type frag_size;
    ref_type next;
    char_type data[];

    blob_storage(ref_type* backref, size_type size, size_type frag_size) noexcept
        : backref(backref)
        , size(size)
        , frag_size(frag_size)
        , next(nullptr)
    {
        *backref = this;
    }

    blob_storage(blob_storage&& o) noexcept
        : backref(o.backref)
        , size(o.size)
        , frag_size(o.frag_size)
        , next(o.next)
    {
        *backref = this;
        o.next = nullptr;
        if (next) {
            next->backref = &next;
        }
        memcpy(data, o.data, frag_size);
    }
} __attribute__((packed));

// A managed version of "bytes" (can be used with LSA).
class managed_bytes {
    static thread_local std::unordered_map<const blob_storage*, std::unique_ptr<bytes_view::value_type[]>> _lc_state;
    struct linearization_context {
        unsigned _nesting = 0;
        // Map from first blob_storage address to linearized version
        // We use the blob_storage address to be insentive to moving
        // a managed_bytes object.
        // linearization_context is entered often in the fast path, but it is
        // actually used only in rare (slow) cases.
        std::unordered_map<const blob_storage*, std::unique_ptr<bytes_view::value_type[]>>* _state_ptr = nullptr;
        void enter() {
            ++_nesting;
        }
        void leave() {
            if (!--_nesting && _state_ptr) {
                _state_ptr->clear();
                _state_ptr = nullptr;
            }
        }
        void forget(const blob_storage* p) noexcept;
    };
    static thread_local linearization_context _linearization_context;
public:
    struct linearization_context_guard {
        linearization_context_guard() {
            _linearization_context.enter();
        }
        ~linearization_context_guard() {
            _linearization_context.leave();
        }
    };
private:
    static constexpr size_t max_inline_size = 15;
    struct small_blob {
        bytes_view::value_type data[max_inline_size];
        int8_t size; // -1 -> use blob_storage
    };
    union u {
        u() {}
        ~u() {}
        blob_storage::ref_type ptr;
        small_blob small;
    } _u;
    static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small");
private:
    bool external() const {
        return _u.small.size < 0;
    }
    size_t max_seg(allocation_strategy& alctr) {
        return alctr.preferred_max_contiguous_allocation() - sizeof(blob_storage);
    }
    void free_chain(blob_storage* p) noexcept {
        if (p->next && _linearization_context._nesting) {
            _linearization_context.forget(p);
        }
        auto& alctr = current_allocator();
        while (p) {
            auto n = p->next;
            alctr.destroy(p);
            p = n;
        }
    }
    const bytes_view::value_type* read_linearize() const {
        seastar::memory::on_alloc_point();
        if (!external()) {
            return _u.small.data;
        } else  if (!_u.ptr->next) {
            return _u.ptr->data;
        } else {
            return do_linearize();
        }
    }
    bytes_view::value_type& value_at_index(blob_storage::size_type index) {
        if (!external()) {
            return _u.small.data[index];
        }
        blob_storage* a = _u.ptr;
        while (index >= a->frag_size) {
            index -= a->frag_size;
            a = a->next;
        }
        return a->data[index];
    }
    std::unique_ptr<bytes_view::value_type[]> do_linearize_pure() const;
    const bytes_view::value_type* do_linearize() const;
public:
    using size_type = blob_storage::size_type;
    struct initialized_later {};

    managed_bytes() {
        _u.small.size = 0;
    }

    managed_bytes(const blob_storage::char_type* ptr, size_type size)
        : managed_bytes(bytes_view(ptr, size)) {}

    managed_bytes(const bytes& b) : managed_bytes(static_cast<bytes_view>(b)) {}

    managed_bytes(initialized_later, size_type size) {
        memory::on_alloc_point();
        if (size <= max_inline_size) {
            _u.small.size = size;
        } else {
            _u.small.size = -1;
            auto& alctr = current_allocator();
            auto maxseg = max_seg(alctr);
            auto now = std::min(size_t(size), maxseg);
            void* p = alctr.alloc(&get_standard_migrator<blob_storage>(),
                sizeof(blob_storage) + now, alignof(blob_storage));
            auto first = new (p) blob_storage(&_u.ptr, size, now);
            auto last = first;
            size -= now;
            try {
                while (size) {
                    auto now = std::min(size_t(size), maxseg);
                    void* p = alctr.alloc(&get_standard_migrator<blob_storage>(),
                        sizeof(blob_storage) + now, alignof(blob_storage));
                    last = new (p) blob_storage(&last->next, 0, now);
                    size -= now;
                }
            } catch (...) {
                free_chain(first);
                throw;
            }
        }
    }

    managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) {
        if (!external()) {
            // Workaround for https://github.com/scylladb/scylla/issues/4086
            #pragma GCC diagnostic push
            #pragma GCC diagnostic ignored "-Warray-bounds"
            std::copy(v.begin(), v.end(), _u.small.data);
            #pragma GCC diagnostic pop
            return;
        }
        auto p = v.data();
        auto s = v.size();
        auto b = _u.ptr;
        while (s) {
            memcpy(b->data, p, b->frag_size);
            p += b->frag_size;
            s -= b->frag_size;
            b = b->next;
        }
        assert(!b);
    }

    managed_bytes(std::initializer_list<bytes::value_type> b) : managed_bytes(b.begin(), b.size()) {}

    ~managed_bytes() noexcept {
        if (external()) {
            free_chain(_u.ptr);
        }
    }

    managed_bytes(const managed_bytes& o) : managed_bytes(initialized_later(), o.size()) {
        if (!o.external()) {
            _u.small = o._u.small;
            return;
        }
        auto s = size();
        const blob_storage::ref_type* next_src = &o._u.ptr;
        blob_storage* blob_src = nullptr;
        size_type size_src = 0;
        size_type offs_src = 0;
        blob_storage::ref_type* next_dst = &_u.ptr;
        blob_storage* blob_dst = nullptr;
        size_type size_dst = 0;
        size_type offs_dst = 0;
        while (s) {
            if (!size_src) {
                blob_src = *next_src;
                next_src = &blob_src->next;
                size_src = blob_src->frag_size;
                offs_src = 0;
            }
            if (!size_dst) {
                blob_dst = *next_dst;
                next_dst = &blob_dst->next;
                size_dst = blob_dst->frag_size;
                offs_dst = 0;
            }
            auto now = std::min(size_src, size_dst);
            memcpy(blob_dst->data + offs_dst, blob_src->data + offs_src, now);
            s -= now;
            offs_src += now; size_src -= now;
            offs_dst += now; size_dst -= now;
        }
        assert(size_src == 0 && size_dst == 0);
    }

    managed_bytes(managed_bytes&& o) noexcept
        : _u(o._u)
    {
        if (external()) {
            // _u.ptr cannot be null
            _u.ptr->backref = &_u.ptr;
        }
        o._u.small.size = 0;
    }

    managed_bytes& operator=(managed_bytes&& o) noexcept {
        if (this != &o) {
            this->~managed_bytes();
            new (this) managed_bytes(std::move(o));
        }
        return *this;
    }

    managed_bytes& operator=(const managed_bytes& o) {
        if (this != &o) {
            managed_bytes tmp(o);
            this->~managed_bytes();
            new (this) managed_bytes(std::move(tmp));
        }
        return *this;
    }

    bool operator==(const managed_bytes& o) const {
        if (size() != o.size()) {
            return false;
        }
        if (!external()) {
            return std::equal(_u.small.data, _u.small.data + _u.small.size, o._u.small.data);
        } else {
            auto a = _u.ptr;
            auto a_data = a->data;
            auto a_remain = a->frag_size;
            a = a->next;
            auto b = o._u.ptr;
            auto b_data = b->data;
            auto b_remain = b->frag_size;
            b = b->next;
            while (a_remain || b_remain) {
                auto now = std::min(a_remain, b_remain);
                if (bytes_view(a_data, now) != bytes_view(b_data, now)) {
                    return false;
                }
                a_data += now;
                a_remain -= now;
                if (!a_remain && a) {
                    a_data = a->data;
                    a_remain = a->frag_size;
                    a = a->next;
                }
                b_data += now;
                b_remain -= now;
                if (!b_remain && b) {
                    b_data = b->data;
                    b_remain = b->frag_size;
                    b = b->next;
                }
            }
            return true;
        }
    }

    bool operator!=(const managed_bytes& o) const {
        return !(*this == o);
    }

    operator bytes_view() const {
        return { data(), size() };
    }

    bool is_fragmented() const {
        return external() && _u.ptr->next;
    }

    operator bytes_mutable_view() {
        assert(!is_fragmented());
        return { data(), size() };
    };

    bytes_view::value_type& operator[](size_type index) {
        return value_at_index(index);
    }

    const bytes_view::value_type& operator[](size_type index) const {
        return const_cast<const bytes_view::value_type&>(
                const_cast<managed_bytes*>(this)->value_at_index(index));
    }

    size_type size() const {
        if (external()) {
            return _u.ptr->size;
        } else {
            return _u.small.size;
        }
    }

    const blob_storage::char_type* begin() const {
        return data();
    }

    const blob_storage::char_type* end() const {
        return data() + size();
    }

    blob_storage::char_type* begin() {
        return data();
    }

    blob_storage::char_type* end() {
        return data() + size();
    }

    bool empty() const {
        return _u.small.size == 0;
    }

    blob_storage::char_type* data() {
        if (external()) {
            assert(!_u.ptr->next);  // must be linearized
            return _u.ptr->data;
        } else {
            return _u.small.data;
        }
    }

    const blob_storage::char_type* data() const {
        return read_linearize();
    }

    // Returns the amount of external memory used.
    size_t external_memory_usage() const {
        if (external()) {
            size_t mem = 0;
            blob_storage* blob = _u.ptr;
            while (blob) {
                mem += blob->frag_size + sizeof(blob_storage);
                blob = blob->next;
            }
            return mem;
        }
        return 0;
    }

    template <std::invocable<> Func>
    friend std::result_of_t<Func()> with_linearized_managed_bytes(Func&& func);
};

// Run func() while ensuring that reads of managed_bytes objects are
// temporarlily linearized
template <std::invocable<> Func>
inline
std::result_of_t<Func()>
with_linearized_managed_bytes(Func&& func) {
    managed_bytes::linearization_context_guard g;
    return func();
}

namespace std {

template <>
struct hash<managed_bytes> {
    size_t operator()(const managed_bytes& v) const {
        return hash<bytes_view>()(v);
    }
};

}

// blob_storage is a variable-size type
inline
size_t
size_for_allocation_strategy(const blob_storage& bs) {
    return sizeof(bs) + bs.frag_size;
}
<commit_msg>utils: managed_bytes: introduce with_linearized()<commit_after>
/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>
#include <memory>
#include "bytes.hh"
#include "utils/allocation_strategy.hh"
#include <seastar/core/unaligned.hh>
#include <seastar/util/alloc_failure_injector.hh>
#include <unordered_map>
#include <type_traits>

struct blob_storage {
    struct [[gnu::packed]] ref_type {
        blob_storage* ptr;

        ref_type() {}
        ref_type(blob_storage* ptr) : ptr(ptr) {}
        operator blob_storage*() const { return ptr; }
        blob_storage* operator->() const { return ptr; }
        blob_storage& operator*() const { return *ptr; }
    };
    using size_type = uint32_t;
    using char_type = bytes_view::value_type;

    ref_type* backref;
    size_type size;
    size_type frag_size;
    ref_type next;
    char_type data[];

    blob_storage(ref_type* backref, size_type size, size_type frag_size) noexcept
        : backref(backref)
        , size(size)
        , frag_size(frag_size)
        , next(nullptr)
    {
        *backref = this;
    }

    blob_storage(blob_storage&& o) noexcept
        : backref(o.backref)
        , size(o.size)
        , frag_size(o.frag_size)
        , next(o.next)
    {
        *backref = this;
        o.next = nullptr;
        if (next) {
            next->backref = &next;
        }
        memcpy(data, o.data, frag_size);
    }
} __attribute__((packed));

// A managed version of "bytes" (can be used with LSA).
class managed_bytes {
    static thread_local std::unordered_map<const blob_storage*, std::unique_ptr<bytes_view::value_type[]>> _lc_state;
    struct linearization_context {
        unsigned _nesting = 0;
        // Map from first blob_storage address to linearized version
        // We use the blob_storage address to be insentive to moving
        // a managed_bytes object.
        // linearization_context is entered often in the fast path, but it is
        // actually used only in rare (slow) cases.
        std::unordered_map<const blob_storage*, std::unique_ptr<bytes_view::value_type[]>>* _state_ptr = nullptr;
        void enter() {
            ++_nesting;
        }
        void leave() {
            if (!--_nesting && _state_ptr) {
                _state_ptr->clear();
                _state_ptr = nullptr;
            }
        }
        void forget(const blob_storage* p) noexcept;
    };
    static thread_local linearization_context _linearization_context;
public:
    struct linearization_context_guard {
        linearization_context_guard() {
            _linearization_context.enter();
        }
        ~linearization_context_guard() {
            _linearization_context.leave();
        }
    };
private:
    static constexpr size_t max_inline_size = 15;
    struct small_blob {
        bytes_view::value_type data[max_inline_size];
        int8_t size; // -1 -> use blob_storage
    };
    union u {
        u() {}
        ~u() {}
        blob_storage::ref_type ptr;
        small_blob small;
    } _u;
    static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small");
private:
    bool external() const {
        return _u.small.size < 0;
    }
    size_t max_seg(allocation_strategy& alctr) {
        return alctr.preferred_max_contiguous_allocation() - sizeof(blob_storage);
    }
    void free_chain(blob_storage* p) noexcept {
        if (p->next && _linearization_context._nesting) {
            _linearization_context.forget(p);
        }
        auto& alctr = current_allocator();
        while (p) {
            auto n = p->next;
            alctr.destroy(p);
            p = n;
        }
    }
    const bytes_view::value_type* read_linearize() const {
        seastar::memory::on_alloc_point();
        if (!external()) {
            return _u.small.data;
        } else  if (!_u.ptr->next) {
            return _u.ptr->data;
        } else {
            return do_linearize();
        }
    }
    bytes_view::value_type& value_at_index(blob_storage::size_type index) {
        if (!external()) {
            return _u.small.data[index];
        }
        blob_storage* a = _u.ptr;
        while (index >= a->frag_size) {
            index -= a->frag_size;
            a = a->next;
        }
        return a->data[index];
    }
    std::unique_ptr<bytes_view::value_type[]> do_linearize_pure() const;
    const bytes_view::value_type* do_linearize() const;
public:
    using size_type = blob_storage::size_type;
    struct initialized_later {};

    managed_bytes() {
        _u.small.size = 0;
    }

    managed_bytes(const blob_storage::char_type* ptr, size_type size)
        : managed_bytes(bytes_view(ptr, size)) {}

    managed_bytes(const bytes& b) : managed_bytes(static_cast<bytes_view>(b)) {}

    managed_bytes(initialized_later, size_type size) {
        memory::on_alloc_point();
        if (size <= max_inline_size) {
            _u.small.size = size;
        } else {
            _u.small.size = -1;
            auto& alctr = current_allocator();
            auto maxseg = max_seg(alctr);
            auto now = std::min(size_t(size), maxseg);
            void* p = alctr.alloc(&get_standard_migrator<blob_storage>(),
                sizeof(blob_storage) + now, alignof(blob_storage));
            auto first = new (p) blob_storage(&_u.ptr, size, now);
            auto last = first;
            size -= now;
            try {
                while (size) {
                    auto now = std::min(size_t(size), maxseg);
                    void* p = alctr.alloc(&get_standard_migrator<blob_storage>(),
                        sizeof(blob_storage) + now, alignof(blob_storage));
                    last = new (p) blob_storage(&last->next, 0, now);
                    size -= now;
                }
            } catch (...) {
                free_chain(first);
                throw;
            }
        }
    }

    managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) {
        if (!external()) {
            // Workaround for https://github.com/scylladb/scylla/issues/4086
            #pragma GCC diagnostic push
            #pragma GCC diagnostic ignored "-Warray-bounds"
            std::copy(v.begin(), v.end(), _u.small.data);
            #pragma GCC diagnostic pop
            return;
        }
        auto p = v.data();
        auto s = v.size();
        auto b = _u.ptr;
        while (s) {
            memcpy(b->data, p, b->frag_size);
            p += b->frag_size;
            s -= b->frag_size;
            b = b->next;
        }
        assert(!b);
    }

    managed_bytes(std::initializer_list<bytes::value_type> b) : managed_bytes(b.begin(), b.size()) {}

    ~managed_bytes() noexcept {
        if (external()) {
            free_chain(_u.ptr);
        }
    }

    managed_bytes(const managed_bytes& o) : managed_bytes(initialized_later(), o.size()) {
        if (!o.external()) {
            _u.small = o._u.small;
            return;
        }
        auto s = size();
        const blob_storage::ref_type* next_src = &o._u.ptr;
        blob_storage* blob_src = nullptr;
        size_type size_src = 0;
        size_type offs_src = 0;
        blob_storage::ref_type* next_dst = &_u.ptr;
        blob_storage* blob_dst = nullptr;
        size_type size_dst = 0;
        size_type offs_dst = 0;
        while (s) {
            if (!size_src) {
                blob_src = *next_src;
                next_src = &blob_src->next;
                size_src = blob_src->frag_size;
                offs_src = 0;
            }
            if (!size_dst) {
                blob_dst = *next_dst;
                next_dst = &blob_dst->next;
                size_dst = blob_dst->frag_size;
                offs_dst = 0;
            }
            auto now = std::min(size_src, size_dst);
            memcpy(blob_dst->data + offs_dst, blob_src->data + offs_src, now);
            s -= now;
            offs_src += now; size_src -= now;
            offs_dst += now; size_dst -= now;
        }
        assert(size_src == 0 && size_dst == 0);
    }

    managed_bytes(managed_bytes&& o) noexcept
        : _u(o._u)
    {
        if (external()) {
            // _u.ptr cannot be null
            _u.ptr->backref = &_u.ptr;
        }
        o._u.small.size = 0;
    }

    managed_bytes& operator=(managed_bytes&& o) noexcept {
        if (this != &o) {
            this->~managed_bytes();
            new (this) managed_bytes(std::move(o));
        }
        return *this;
    }

    managed_bytes& operator=(const managed_bytes& o) {
        if (this != &o) {
            managed_bytes tmp(o);
            this->~managed_bytes();
            new (this) managed_bytes(std::move(tmp));
        }
        return *this;
    }

    bool operator==(const managed_bytes& o) const {
        if (size() != o.size()) {
            return false;
        }
        if (!external()) {
            return std::equal(_u.small.data, _u.small.data + _u.small.size, o._u.small.data);
        } else {
            auto a = _u.ptr;
            auto a_data = a->data;
            auto a_remain = a->frag_size;
            a = a->next;
            auto b = o._u.ptr;
            auto b_data = b->data;
            auto b_remain = b->frag_size;
            b = b->next;
            while (a_remain || b_remain) {
                auto now = std::min(a_remain, b_remain);
                if (bytes_view(a_data, now) != bytes_view(b_data, now)) {
                    return false;
                }
                a_data += now;
                a_remain -= now;
                if (!a_remain && a) {
                    a_data = a->data;
                    a_remain = a->frag_size;
                    a = a->next;
                }
                b_data += now;
                b_remain -= now;
                if (!b_remain && b) {
                    b_data = b->data;
                    b_remain = b->frag_size;
                    b = b->next;
                }
            }
            return true;
        }
    }

    bool operator!=(const managed_bytes& o) const {
        return !(*this == o);
    }

    operator bytes_view() const {
        return { data(), size() };
    }

    bool is_fragmented() const {
        return external() && _u.ptr->next;
    }

    operator bytes_mutable_view() {
        assert(!is_fragmented());
        return { data(), size() };
    };

    bytes_view::value_type& operator[](size_type index) {
        return value_at_index(index);
    }

    const bytes_view::value_type& operator[](size_type index) const {
        return const_cast<const bytes_view::value_type&>(
                const_cast<managed_bytes*>(this)->value_at_index(index));
    }

    size_type size() const {
        if (external()) {
            return _u.ptr->size;
        } else {
            return _u.small.size;
        }
    }

    const blob_storage::char_type* begin() const {
        return data();
    }

    const blob_storage::char_type* end() const {
        return data() + size();
    }

    blob_storage::char_type* begin() {
        return data();
    }

    blob_storage::char_type* end() {
        return data() + size();
    }

    bool empty() const {
        return _u.small.size == 0;
    }

    blob_storage::char_type* data() {
        if (external()) {
            assert(!_u.ptr->next);  // must be linearized
            return _u.ptr->data;
        } else {
            return _u.small.data;
        }
    }

    const blob_storage::char_type* data() const {
        return read_linearize();
    }

    // Returns the amount of external memory used.
    size_t external_memory_usage() const {
        if (external()) {
            size_t mem = 0;
            blob_storage* blob = _u.ptr;
            while (blob) {
                mem += blob->frag_size + sizeof(blob_storage);
                blob = blob->next;
            }
            return mem;
        }
        return 0;
    }

    template <std::invocable<bytes_view> Func>
    std::invoke_result_t<Func, bytes_view> with_linearized(Func&& func) const {
        const bytes_view::value_type* start = nullptr;
        size_t size = 0;
        if (!external()) {
            start = _u.small.data;
            size = _u.small.size;
        } else if (!_u.ptr->next) {
            start = _u.ptr->data;
            size = _u.ptr->size;
        }
        if (start) {
            return func(bytes_view(start, size));
        } else {
            auto data = do_linearize_pure();
            return func(bytes_view(data.get(), _u.ptr->size));
        }
    }

    template <std::invocable<> Func>
    friend std::result_of_t<Func()> with_linearized_managed_bytes(Func&& func);
};

// Run func() while ensuring that reads of managed_bytes objects are
// temporarlily linearized
template <std::invocable<> Func>
inline
std::result_of_t<Func()>
with_linearized_managed_bytes(Func&& func) {
    managed_bytes::linearization_context_guard g;
    return func();
}

namespace std {

template <>
struct hash<managed_bytes> {
    size_t operator()(const managed_bytes& v) const {
        return hash<bytes_view>()(v);
    }
};

}

// blob_storage is a variable-size type
inline
size_t
size_for_allocation_strategy(const blob_storage& bs) {
    return sizeof(bs) + bs.frag_size;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>

#include <UnitTest++.h>
#include "configparse.h"

std::string *config_path = (std::string*)0;

class CustomFileWMConfig : public WMConfig
{
protected:
    std::string get_config_path() const
    {
        return *config_path;
    };
};

struct WMConfigFixture
{
    WMConfigFixture()
    {};

    ~WMConfigFixture()
    {};

    CustomFileWMConfig config;
};

void write_config_file(std::string filename, const char *text)
{
    std::ofstream config(filename.c_str());
    config << text;
    config.close();
}

SUITE(WMConfigSuite)
{
    TEST_FIXTURE(WMConfigFixture, test_default_shell)
    {
        // Ensure that an empty config file provides the default shell
        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(std::string("/usr/bin/xterm"), config.shell);
    }

    TEST_FIXTURE(WMConfigFixture, test_shell)
    {
        // Make sure that any particular setting is acceptable
        write_config_file(*config_path,
            "[smallwm]\nshell=some-terminal\n");

        config.load();
        CHECK_EQUAL(std::string("some-terminal"), config.shell);
    }

    TEST_FIXTURE(WMConfigFixture, test_shell_empty)
    {
        // Make sure that an empty configuration option is not accepted
        write_config_file(*config_path,
            "[smallwm]\nshell=\n");

        config.load();
        CHECK_EQUAL(std::string("/usr/bin/xterm"), config.shell);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_num_desktops)
    {
        // Ensure that an empty config file provides the default number of
        // desktops
        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(5, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_num_desktops)
    {
        // Make sure that a valid number of desktops sets the option
        write_config_file(*config_path,
            "[smallwm]\ndesktops=42\n");

        config.load();
        CHECK_EQUAL(42, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_num_desktops_zero)
    {
        // Zero desktops are invalid, so make sure they revert to the default
        write_config_file(*config_path,
            "[smallwm]\ndesktops=0\n");

        config.load();
        CHECK_EQUAL(5, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_num_desktops_negative)
    {
        // Negative desktops are also invalid
        write_config_file(*config_path,
            "[smallwm]\ndesktops=-32\n");

        config.load();
        CHECK_EQUAL(5, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_icon_width)
    {

        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(75, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_width)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-width=42\n");

        config.load();
        CHECK_EQUAL(42, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_width_zero)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-width=0\n");

        config.load();
        CHECK_EQUAL(75, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_width_negative)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-width=-42\n");

        config.load();
        CHECK_EQUAL(75, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_icon_height)
    {

        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(20, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_height)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-height=42\n");

        config.load();
        CHECK_EQUAL(42, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_height_zero)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-height=0\n");

        config.load();
        CHECK_EQUAL(20, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_height_negative)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-height=-42\n");

        config.load();
        CHECK_EQUAL(20, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_border_width)
    {

        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(4, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_border_width)
    {
        write_config_file(*config_path,
            "[smallwm]\nborder-width=42\n");

        config.load();
        CHECK_EQUAL(42, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_border_width_zero)
    {
        write_config_file(*config_path,
            "[smallwm]\nborder-width=0\n");

        config.load();
        CHECK_EQUAL(4, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_border_width_negative)
    {
        write_config_file(*config_path,
            "[smallwm]\nborder-width=-42\n");

        config.load();
        CHECK_EQUAL(4, config.border_width);
    }
};

int main()
{
    char *filename = tempnam("/tmp", "smallwm");
    config_path = new std::string(filename);
    std::free(filename);

    UnitTest::RunAllTests();
}
<commit_msg>The new tests for the 'icons-icon' option passes.<commit_after>#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>

#include <UnitTest++.h>
#include "configparse.h"

std::string *config_path = (std::string*)0;

class CustomFileWMConfig : public WMConfig
{
protected:
    std::string get_config_path() const
    {
        return *config_path;
    };
};

struct WMConfigFixture
{
    WMConfigFixture()
    {};

    ~WMConfigFixture()
    {};

    CustomFileWMConfig config;
};

void write_config_file(std::string filename, const char *text)
{
    std::ofstream config(filename.c_str());
    config << text;
    config.close();
}

SUITE(WMConfigSuite)
{
    TEST_FIXTURE(WMConfigFixture, test_default_shell)
    {
        // Ensure that an empty config file provides the default shell
        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(std::string("/usr/bin/xterm"), config.shell);
    }

    TEST_FIXTURE(WMConfigFixture, test_shell)
    {
        // Make sure that any particular setting is acceptable
        write_config_file(*config_path,
            "[smallwm]\nshell=some-terminal\n");

        config.load();
        CHECK_EQUAL(std::string("some-terminal"), config.shell);
    }

    TEST_FIXTURE(WMConfigFixture, test_shell_empty)
    {
        // Make sure that an empty configuration option is not accepted
        write_config_file(*config_path,
            "[smallwm]\nshell=\n");

        config.load();
        CHECK_EQUAL(std::string("/usr/bin/xterm"), config.shell);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_num_desktops)
    {
        // Ensure that an empty config file provides the default number of
        // desktops
        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(5, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_num_desktops)
    {
        // Make sure that a valid number of desktops sets the option
        write_config_file(*config_path,
            "[smallwm]\ndesktops=42\n");

        config.load();
        CHECK_EQUAL(42, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_num_desktops_zero)
    {
        // Zero desktops are invalid, so make sure they revert to the default
        write_config_file(*config_path,
            "[smallwm]\ndesktops=0\n");

        config.load();
        CHECK_EQUAL(5, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_num_desktops_negative)
    {
        // Negative desktops are also invalid
        write_config_file(*config_path,
            "[smallwm]\ndesktops=-32\n");

        config.load();
        CHECK_EQUAL(5, config.num_desktops);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_icon_width)
    {

        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(75, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_width)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-width=42\n");

        config.load();
        CHECK_EQUAL(42, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_width_zero)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-width=0\n");

        config.load();
        CHECK_EQUAL(75, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_width_negative)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-width=-42\n");

        config.load();
        CHECK_EQUAL(75, config.icon_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_icon_height)
    {

        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(20, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_height)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-height=42\n");

        config.load();
        CHECK_EQUAL(42, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_height_zero)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-height=0\n");

        config.load();
        CHECK_EQUAL(20, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_height_negative)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-height=-42\n");

        config.load();
        CHECK_EQUAL(20, config.icon_height);
    }

    TEST_FIXTURE(WMConfigFixture, test_default_border_width)
    {

        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(4, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_border_width)
    {
        write_config_file(*config_path,
            "[smallwm]\nborder-width=42\n");

        config.load();
        CHECK_EQUAL(42, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_border_width_zero)
    {
        write_config_file(*config_path,
            "[smallwm]\nborder-width=0\n");

        config.load();
        CHECK_EQUAL(4, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_border_width_negative)
    {
        write_config_file(*config_path,
            "[smallwm]\nborder-width=-42\n");

        config.load();
        CHECK_EQUAL(4, config.border_width);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_icons_default)
    {
        write_config_file(*config_path, "\n");

        config.load();
        CHECK_EQUAL(true, config.show_icons);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_icons_yes)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-icons=1\n");

        config.load();
        CHECK_EQUAL(true, config.show_icons);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_icons_no)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-icons=0\n");

        config.load();
        CHECK_EQUAL(false, config.show_icons);
    }

    TEST_FIXTURE(WMConfigFixture, test_icon_icons_invalid)
    {
        write_config_file(*config_path,
            "[smallwm]\nicon-icons=42\n");

        config.load();
        CHECK_EQUAL(true, config.show_icons);
    }
};

int main()
{
    char *filename = tempnam("/tmp", "smallwm");
    config_path = new std::string(filename);
    std::free(filename);

    UnitTest::RunAllTests();
    remove(config_path->c_str());
}
<|endoftext|>


<commit_before>#include "SoloFeature.h"
#include "serviceFuns.cpp"
#include <math.h>

void SoloFeature::cellFiltering()
{

    if (pSolo.cellFilter.type[0]=="None" ||  nCB<1) {//no filtering, or no cells to filter
        return;
    };

    switch(featureType) {
        default:
            return; //filtering not done for other features

        case SoloFeatureTypes::Velocyto: {
            filteredCells.reset(nCB);

            //Velocyto: use filter from Gene
            SoloFeature &soFeGe= *soloFeatAll[pSolo.featureInd[SoloFeatureTypes::Gene]];
            for (uint32 ic=0; ic<soFeGe.nCB; ic++) {
                if (soFeGe.filteredCells.filtVecBool[ic] && indCBwl[soFeGe.indCB[ic]] != (uint32)-1) {
                    //this cell passde Gene filtering, and is detected in Velocyto
                    filteredCells.filtVecBool[indCBwl[soFeGe.indCB[ic]]] = true;
                };
            };

            nUMIperCBsorted=nUMIperCB;
            std::sort( nUMIperCBsorted.begin(), nUMIperCBsorted.end(), [](const uint32_t &u1, const uint32_t &u2) {return u1>u2;} ); //sort descending
            break;
        };
        case SoloFeatureTypes::Gene:
        case SoloFeatureTypes::GeneFull:
        case SoloFeatureTypes::GeneFull_Ex50pAS:
        case SoloFeatureTypes::GeneFull_ExonOverIntron:
        case -1: //undefined: matrix loaded from file
        {
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// cell calling (filtering)
            //do the filtering

            //simple filtering first
            nUMIperCBsorted=nUMIperCB;
            std::sort( nUMIperCBsorted.begin(), nUMIperCBsorted.end(), [](const uint32_t &u1, const uint32_t &u2) {return u1>u2;} ); //sort descending

            uint32 nUMImax=0, nUMImin=0;
            if (pSolo.cellFilter.type[0]=="TopCells") {
                nUMImin = nUMIperCBsorted[min(nCB-1,pSolo.cellFilter.topCells)];
            } else {//other filtering types require simple filtering first
                //find robust max
                uint32 maxind=int(round(pSolo.cellFilter.knee.nExpectedCells*(1.0-pSolo.cellFilter.knee.maxPercentile)));//cell number for robust max
                nUMImax = nUMIperCBsorted[min(nCB-1,maxind)];//robust estimate of the max UMI
                nUMImin = int(round(nUMImax/pSolo.cellFilter.knee.maxMinRatio));
            };
            nUMImin=max(nUMImin,(uint32) 1);//cannot be zero

            filteredCells.reset(nCB); //all stats to 0

            for (uint32 icb=0; icb<nCB; icb++) {
                if (nUMIperCB[icb]>=nUMImin) {
                    filteredCells.filtVecBool[icb]=true;
                    filteredCells.nCellsSimple++;
                };
            };

            P.inOut->logMain << "cellFiltering: simple: nUMImax="<< nUMImax <<"; nUMImin="<< nUMImin <<"; nCellsSimple="<< filteredCells.nCellsSimple <<endl;

            if (pSolo.cellFilter.type[0]=="EmptyDrops_CR") {
                emptyDrops_CR();
            };
        };
    };
    //filtering is done: filtVecBool=true for kept cells

    //calculate filtered statistics

    bool *geneDetected = new bool[featuresNumber]; //=true if a gene was detected in at least one cell
    memset((void*) geneDetected, 0, featuresNumber);

    for (uint32 icb=0; icb<nCB; icb++) {
        if (filteredCells.filtVecBool[icb]) {

            filteredCells.nCells++;

            filteredCells.nUMIinCells += nUMIperCB[icb]; //nUMIperCB was calculated for umiDedup-main

            if (nReadPerCBunique.size()>0) {//for CellFiltering only, read information is not available
                filteredCells.nReadInCells += nReadPerCBtotal[icb];
                filteredCells.nReadInCellsUnique += nReadPerCBunique[icb];
                filteredCells.nReadPerCellUnique.push_back(nReadPerCBunique[icb]);
            };

            uint32 ng1 = 0; //number of genes detected in this cell
            for (uint32 ig=0; ig<nGenePerCB[icb]; ig++) {
                uint32 indG1=countCellGeneUMIindex[icb]+ig*countMatStride;
                if (countCellGeneUMI[indG1 + pSolo.umiDedup.countInd.main] > 0) {
                    geneDetected[countCellGeneUMI[indG1]] = true; //gene is present if it's count > 0 for
                    ng1++;
                };
            };

            filteredCells.nGeneInCells += ng1; //had to recalculate this since some gene counts could be 0
            filteredCells.nGenePerCell.push_back(ng1);
        };
    };

    if (filteredCells.nCells==0) {//all stats were already set to 0
        return;
    };

    filteredCells.nGeneDetected=0;
    for (uint32 ii=0; ii<featuresNumber; ii++) {
        if (geneDetected[ii])
            filteredCells.nGeneDetected++;
    };

    filteredCells.meanUMIperCell = filteredCells.nUMIinCells / filteredCells.nCells;
    filteredCells.meanReadPerCellUnique = filteredCells.nReadInCellsUnique / filteredCells.nCells;
    filteredCells.meanGenePerCell = filteredCells.nGeneInCells / filteredCells.nCells;

    sort(filteredCells.nReadPerCellUnique.begin(), filteredCells.nReadPerCellUnique.end());
    sort(filteredCells.nGenePerCell.begin(), filteredCells.nGenePerCell.end());

    filteredCells.medianUMIperCell = nUMIperCBsorted[filteredCells.nCells/2];
    filteredCells.medianGenePerCell = filteredCells.nGenePerCell[filteredCells.nCells/2];
    filteredCells.medianReadPerCellUnique = filteredCells.nReadPerCellUnique[filteredCells.nCells/2];

    //////////////////////////////////////////////////////////////////output filtered matrix
    outputResults(true, outputPrefixFiltered);

    return;
};
<commit_msg>#1619: fixed memory leak in SoloFeature_cellFiltering.cpp<commit_after>#include "SoloFeature.h"
#include "serviceFuns.cpp"
#include <math.h>

void SoloFeature::cellFiltering()
{

    if (pSolo.cellFilter.type[0]=="None" ||  nCB<1) {//no filtering, or no cells to filter
        return;
    };

    switch(featureType) {
        default:
            return; //filtering not done for other features

        case SoloFeatureTypes::Velocyto: {
            filteredCells.reset(nCB);

            //Velocyto: use filter from Gene
            SoloFeature &soFeGe= *soloFeatAll[pSolo.featureInd[SoloFeatureTypes::Gene]];
            for (uint32 ic=0; ic<soFeGe.nCB; ic++) {
                if (soFeGe.filteredCells.filtVecBool[ic] && indCBwl[soFeGe.indCB[ic]] != (uint32)-1) {
                    //this cell passde Gene filtering, and is detected in Velocyto
                    filteredCells.filtVecBool[indCBwl[soFeGe.indCB[ic]]] = true;
                };
            };

            nUMIperCBsorted=nUMIperCB;
            std::sort( nUMIperCBsorted.begin(), nUMIperCBsorted.end(), [](const uint32_t &u1, const uint32_t &u2) {return u1>u2;} ); //sort descending
            break;
        };
        case SoloFeatureTypes::Gene:
        case SoloFeatureTypes::GeneFull:
        case SoloFeatureTypes::GeneFull_Ex50pAS:
        case SoloFeatureTypes::GeneFull_ExonOverIntron:
        case -1: //undefined: matrix loaded from file
        {
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// cell calling (filtering)
            //do the filtering

            //simple filtering first
            nUMIperCBsorted=nUMIperCB;
            std::sort( nUMIperCBsorted.begin(), nUMIperCBsorted.end(), [](const uint32_t &u1, const uint32_t &u2) {return u1>u2;} ); //sort descending

            uint32 nUMImax=0, nUMImin=0;
            if (pSolo.cellFilter.type[0]=="TopCells") {
                nUMImin = nUMIperCBsorted[min(nCB-1,pSolo.cellFilter.topCells)];
            } else {//other filtering types require simple filtering first
                //find robust max
                uint32 maxind=int(round(pSolo.cellFilter.knee.nExpectedCells*(1.0-pSolo.cellFilter.knee.maxPercentile)));//cell number for robust max
                nUMImax = nUMIperCBsorted[min(nCB-1,maxind)];//robust estimate of the max UMI
                nUMImin = int(round(nUMImax/pSolo.cellFilter.knee.maxMinRatio));
            };
            nUMImin=max(nUMImin,(uint32) 1);//cannot be zero

            filteredCells.reset(nCB); //all stats to 0

            for (uint32 icb=0; icb<nCB; icb++) {
                if (nUMIperCB[icb]>=nUMImin) {
                    filteredCells.filtVecBool[icb]=true;
                    filteredCells.nCellsSimple++;
                };
            };

            P.inOut->logMain << "cellFiltering: simple: nUMImax="<< nUMImax <<"; nUMImin="<< nUMImin <<"; nCellsSimple="<< filteredCells.nCellsSimple <<endl;

            if (pSolo.cellFilter.type[0]=="EmptyDrops_CR") {
                emptyDrops_CR();
            };
        };
    };
    //filtering is done: filtVecBool=true for kept cells

    //calculate filtered statistics

    std::vector<uint32_t> geneDetected(featuresNumber, 0); //=1 if a gene was detected in at least one cell

    for (uint32 icb=0; icb<nCB; icb++) {
        if (filteredCells.filtVecBool[icb]) {

            filteredCells.nCells++;

            filteredCells.nUMIinCells += nUMIperCB[icb]; //nUMIperCB was calculated for umiDedup-main

            if (nReadPerCBunique.size()>0) {//for CellFiltering only, read information is not available
                filteredCells.nReadInCells += nReadPerCBtotal[icb];
                filteredCells.nReadInCellsUnique += nReadPerCBunique[icb];
                filteredCells.nReadPerCellUnique.push_back(nReadPerCBunique[icb]);
            };

            uint32 ng1 = 0; //number of genes detected in this cell
            for (uint32 ig=0; ig<nGenePerCB[icb]; ig++) {
                uint32 indG1=countCellGeneUMIindex[icb]+ig*countMatStride;
                if (countCellGeneUMI[indG1 + pSolo.umiDedup.countInd.main] > 0) {
                    geneDetected[countCellGeneUMI[indG1]] = 1; //gene is present if it's count > 0 for
                    ng1++;
                };
            };

            filteredCells.nGeneInCells += ng1; //had to recalculate this since some gene counts could be 0
            filteredCells.nGenePerCell.push_back(ng1);
        };
    };

    if (filteredCells.nCells==0) {//all stats were already set to 0
        return;
    };

    filteredCells.nGeneDetected=0;
    for (uint32 ii=0; ii<featuresNumber; ii++) {
        if (geneDetected[ii]>0)
            filteredCells.nGeneDetected++;
    };

    filteredCells.meanUMIperCell = filteredCells.nUMIinCells / filteredCells.nCells;
    filteredCells.meanReadPerCellUnique = filteredCells.nReadInCellsUnique / filteredCells.nCells;
    filteredCells.meanGenePerCell = filteredCells.nGeneInCells / filteredCells.nCells;

    std::sort(filteredCells.nReadPerCellUnique.begin(), filteredCells.nReadPerCellUnique.end());
    std::sort(filteredCells.nGenePerCell.begin(), filteredCells.nGenePerCell.end());

    filteredCells.medianUMIperCell = nUMIperCBsorted[filteredCells.nCells/2];
    filteredCells.medianGenePerCell = filteredCells.nGenePerCell[filteredCells.nCells/2];
    filteredCells.medianReadPerCellUnique = filteredCells.nReadPerCellUnique[filteredCells.nCells/2];

    //////////////////////////////////////////////////////////////////output filtered matrix
    outputResults(true, outputPrefixFiltered);

    return;
};
<|endoftext|>


<commit_before>#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>
#include <string>
#include <fstream>
#include <limits>
#include <iostream>
#include <sstream>
#include <cstring>

template <size_t NDIMS>
class Upwind {
public:
  // ctor
  Upwind(const std::vector<double>& velocity,
         const std::vector<double>& lengths,
         const std::vector<int>& numCells) {

    this->numCells = numCells;
    this->deltas.resize(NDIMS);
    this->upDirection.resize(NDIMS);
    this->v = velocity;
    this->lengths = lengths;
    this->ntot = 1;
    for (size_t j = 0; j < NDIMS; ++j) {
      this->upDirection[j] = -1;
      if (velocity[j] < 0.) this->upDirection[j] = +1;
      this->deltas[j] = lengths[j] / numCells[j];
      this->ntot *= numCells[j];
    }
    this->dimProd.resize(NDIMS);
    this->dimProd[NDIMS - 1] = 1;
    for (int i = (int) NDIMS - 2; i >= 0; --i) {
        // last index varies fastest
        this->dimProd[i] =  this->dimProd[i + 1] * this->numCells[i + 1];
      }
    this->f.resize(this->ntot, 0.0);
    // initialize lower corner to one
    this->f[0] = 1;

    this->coeff.resize(NDIMS);
    for (size_t j = 0; j < NDIMS; ++j) {
      this->coeff[j] = this->v[j] * this->upDirection[j] / this->deltas[j];
    }
  }

  void advect(double deltaTime) {

    // OpenACC works with primitive arrays
    double* fPtr = &this->f.front();
    double* coeffPtr = &this->coeff.front();
    int* upDirectionPtr = &this->upDirection.front();
    int* dimProdPtr = &this->dimProd.front();
    int* numCellsPtr = &this->numCells.front();
    int ntot = this->ntot;
    int upI;

    double* fOldPtr = new double[ntot];
    int inds[NDIMS];

#pragma acc data \
  copy(fPtr[ntot]) \
  copyin(fOldPtr[ntot]) \
  copyin(coeffPtr[NDIMS], dimProdPtr[NDIMS],    \
  upDirectionPtr[NDIMS], numCellsPtr[NDIMS], deltaTime)
  {

#pragma acc parallel loop
    for (int i = 0; i < ntot; ++i) {
        fOldPtr[i] = fPtr[i];
    }

#pragma acc parallel loop
    for (int i = 0; i < ntot; ++i) {

#include "compute_index_set.h"

#include "compute_flat_index_offset_x.h"
fPtr[i] -= deltaTime * coeffPtr[0] * (fOldPtr[upI] - fOldPtr[i]);

#include "compute_flat_index_offset_y.h"
fPtr[i] -= deltaTime * coeffPtr[1] * (fOldPtr[upI] - fOldPtr[i]);

#include "compute_flat_index_offset_z.h"
fPtr[i] -= deltaTime * coeffPtr[2] * (fOldPtr[upI] - fOldPtr[i]);

     }
  } // acc data
     delete[] fOldPtr;
  }

#include "saveVTK.h"

  double checksum() const {
    return std::accumulate(this->f.begin(), this->f.end(), 0.0, std::plus<double>());
  }

  void print() const {
    for (size_t i = 0; i < this->f.size(); ++i) {
      std::cout << i << " " << this->f[i] << '\n';
    }
  }

private:
  std::vector<double> f;
  std::vector<double> v;
  std::vector<double> lengths;
  std::vector<double> deltas;
  std::vector<double> coeff;
  std::vector<int> upDirection;
  std::vector<int> dimProd;
  std::vector<int> numCells;
  int ntot;
};

int main(int argc, char** argv) {

  const int ndims = 3;

  if (argc < 2) {
    std::cout << "must specify number of cells in each direction.\n";
    return 1;
  }

  int numTimeSteps = 100;
  if (argc > 2) {
    numTimeSteps = atoi(argv[2]);
  }

  bool doVtk = false;
  if (argc > 3 && strcmp(argv[3], "vtk") == 0) {
    doVtk = true;
  }

  // same resolution in each direction
  std::vector<int> numCells(ndims, atoi(argv[1]));
  std::cout << "number of cells: ";
  for (size_t i = 0; i < numCells.size(); ++i) {
    std::cout << ' ' << numCells[i];
  }
  std::cout << '\n';
  std::cout << "number of time steps: " << numTimeSteps << '\n';

  std::vector<double> velocity(numCells.size(), 1.0);
  std::vector<double> lengths(numCells.size(), 1.0);

  // compute dt
  double courant = 0.1;
  double dt = std::numeric_limits<double>::max();
  for (size_t j = 0; j < velocity.size(); ++j) {
    double dx = lengths[j]/numCells[j];
    double val = courant * dx / velocity[j];
    dt = (val < dt? val: dt);
  }

  Upwind<ndims> up(velocity, lengths, numCells);
  //up.saveVTK("up0.vtk");
  for (int i = 0; i < numTimeSteps; ++i) {
    up.advect(dt);
  }
  std::cout << "check sum: " << up.checksum() << '\n';
  if (doVtk) {
    up.saveVTK("up.vtk");
  }
}
<commit_msg>move copy loop to device<commit_after>#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>
#include <string>
#include <fstream>
#include <limits>
#include <iostream>
#include <sstream>
#include <cstring>

template <size_t NDIMS>
class Upwind {
public:
  // ctor
  Upwind(const std::vector<double>& velocity,
         const std::vector<double>& lengths,
         const std::vector<int>& numCells) {

    this->numCells = numCells;
    this->deltas.resize(NDIMS);
    this->upDirection.resize(NDIMS);
    this->v = velocity;
    this->lengths = lengths;
    this->ntot = 1;
    for (size_t j = 0; j < NDIMS; ++j) {
      this->upDirection[j] = -1;
      if (velocity[j] < 0.) this->upDirection[j] = +1;
      this->deltas[j] = lengths[j] / numCells[j];
      this->ntot *= numCells[j];
    }
    this->dimProd.resize(NDIMS);
    this->dimProd[NDIMS - 1] = 1;
    for (int i = (int) NDIMS - 2; i >= 0; --i) {
        // last index varies fastest
        this->dimProd[i] =  this->dimProd[i + 1] * this->numCells[i + 1];
      }
    this->f.resize(this->ntot, 0.0);
    // initialize lower corner to one
    this->f[0] = 1;

    this->coeff.resize(NDIMS);
    for (size_t j = 0; j < NDIMS; ++j) {
      this->coeff[j] = this->v[j] * this->upDirection[j] / this->deltas[j];
    }
  }

  void advect(double deltaTime) {

    // OpenACC works with primitive arrays
    double* fPtr = &this->f.front();
    double* coeffPtr = &this->coeff.front();
    int* upDirectionPtr = &this->upDirection.front();
    int* dimProdPtr = &this->dimProd.front();
    int* numCellsPtr = &this->numCells.front();
    int ntot = this->ntot;
    int upI;

    double* fOldPtr = new double[ntot];

#pragma acc data \
  copy(fPtr[ntot]) \
  copyin(fOldPtr[ntot]) \
  copyin(coeffPtr[NDIMS], dimProdPtr[NDIMS],	\
  upDirectionPtr[NDIMS], numCellsPtr[NDIMS], deltaTime)
   {

#pragma acc parallel loop
    for (int i = 0; i < ntot; ++i) {
      fOldPtr[i] = fPtr[i];
    }

#pragma acc parallel loop
    for (int i = 0; i < ntot; ++i) {

      int inds[NDIMS];

#include "compute_index_set.h"

#include "compute_flat_index_offset_x.h"
fPtr[i] -= deltaTime * coeffPtr[0] * (fOldPtr[upI] - fOldPtr[i]);

#include "compute_flat_index_offset_y.h"
fPtr[i] -= deltaTime * coeffPtr[1] * (fOldPtr[upI] - fOldPtr[i]);

#include "compute_flat_index_offset_z.h"
fPtr[i] -= deltaTime * coeffPtr[2] * (fOldPtr[upI] - fOldPtr[i]);

     } // acc parallel loop
    } // acc data

    delete[] fOldPtr;
  }

#include "saveVTK.h"

  double checksum() const {
    return std::accumulate(this->f.begin(), this->f.end(), 0.0, std::plus<double>());
  }

  void print() const {
    for (size_t i = 0; i < this->f.size(); ++i) {
      std::cout << i << " " << this->f[i] << '\n';
    }
  }

private:
  std::vector<double> f;
  std::vector<double> v;
  std::vector<double> lengths;
  std::vector<double> deltas;
  std::vector<double> coeff;
  std::vector<int> upDirection;
  std::vector<int> dimProd;
  std::vector<int> numCells;
  int ntot;
};

int main(int argc, char** argv) {

  const int ndims = 3;

  if (argc < 2) {
    std::cout << "must specify number of cells in each direction.\n";
    return 1;
  }

  int numTimeSteps = 100;
  if (argc > 2) {
    numTimeSteps = atoi(argv[2]);
  }

  bool doVtk = false;
  if (argc > 3 && strcmp(argv[3], "vtk") == 0) {
    doVtk = true;
  }

  // same resolution in each direction
  std::vector<int> numCells(ndims, atoi(argv[1]));
  std::cout << "number of cells: ";
  for (size_t i = 0; i < numCells.size(); ++i) {
    std::cout << ' ' << numCells[i];
  }
  std::cout << '\n';
  std::cout << "number of time steps: " << numTimeSteps << '\n';

  std::vector<double> velocity(numCells.size(), 1.0);
  std::vector<double> lengths(numCells.size(), 1.0);

  // compute dt
  double courant = 0.1;
  double dt = std::numeric_limits<double>::max();
  for (size_t j = 0; j < velocity.size(); ++j) {
    double dx = lengths[j]/numCells[j];
    double val = courant * dx / velocity[j];
    dt = (val < dt? val: dt);
  }

  Upwind<ndims> up(velocity, lengths, numCells);
  //up.saveVTK("up0.vtk");
  for (int i = 0; i < numTimeSteps; ++i) {
    up.advect(dt);
  }
  std::cout << "check sum: " << up.checksum() << '\n';
  if (doVtk) {
    up.saveVTK("up.vtk");
  }
}
<|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 22.11.2018
/// @brief Python loader for blue-sky-re kernel
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/common.h>
#include <bs/kernel.h>
#include <bs/detail/lib_descriptor.h>
#include <bs/log.h>

#include <pybind11/pybind11.h>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>

#include <string>

/// TARGET_NAME must match built library name
#ifndef TARGET_NAME
#define TARGET_NAME bs
#endif

#define S_TARGET_NAME BOOST_PP_STRINGIZE(TARGET_NAME)
#define INIT_FN_NAME BOOST_PP_CAT(init, TARGET_NAME)

using namespace blue_sky;
using namespace std;
namespace py = pybind11;

PYBIND11_MODULE(TARGET_NAME, m) {
	// search for BlueSky's kernel plugin descriptor
	BS_GET_PLUGIN_DESCRIPTOR get_pd_fn;
	if(detail::lib_descriptor::load_sym_glob("bs_get_plugin_descriptor", get_pd_fn) != 0 || !get_pd_fn) {
		throw error("BlueSky kernel descriptor wasn't found or invalid!");
	}
	plugin_descriptor* kernel_pd = get_pd_fn();
	// Python namespace must match TARGET_NAME (otherwise Python throws an error)
	kernel_pd->py_namespace = S_TARGET_NAME;
	m.doc() = kernel_pd->description;

	//load plugins with Python subsystem
	give_kernel::Instance().load_plugins(&m);
}

<commit_msg>bspy_laoder: configure kernel before loading kernel Py module<commit_after>/// @file
/// @author uentity
/// @date 22.11.2018
/// @brief Python loader for blue-sky-re kernel
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/common.h>
#include <bs/kernel.h>
#include <bs/detail/lib_descriptor.h>
#include <bs/log.h>

#include <pybind11/pybind11.h>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>

#include <string>

/// TARGET_NAME must match built library name
#ifndef TARGET_NAME
#define TARGET_NAME bs
#endif

#define S_TARGET_NAME BOOST_PP_STRINGIZE(TARGET_NAME)
#define INIT_FN_NAME BOOST_PP_CAT(init, TARGET_NAME)

using namespace blue_sky;
using namespace std;
namespace py = pybind11;

PYBIND11_MODULE(TARGET_NAME, m) {
	// search for BlueSky's kernel plugin descriptor
	BS_GET_PLUGIN_DESCRIPTOR get_pd_fn;
	if(detail::lib_descriptor::load_sym_glob("bs_get_plugin_descriptor", get_pd_fn) != 0 || !get_pd_fn) {
		throw error("BlueSky kernel descriptor wasn't found or invalid!");
	}
	plugin_descriptor* kernel_pd = get_pd_fn();
	// Python namespace must match TARGET_NAME (otherwise Python throws an error)
	kernel_pd->py_namespace = S_TARGET_NAME;
	m.doc() = kernel_pd->description;

	// configure kernel
	BS_KERNEL.configure();

	//load plugins with Python subsystem
	BS_KERNEL.load_plugins(&m);
}

<|endoftext|>


<commit_before>#include "common/http/conn_pool_base.h"

#include "common/common/assert.h"
#include "common/http/utility.h"
#include "common/network/transport_socket_options_impl.h"
#include "common/runtime/runtime_features.h"
#include "common/stats/timespan_impl.h"
#include "common/upstream/upstream_impl.h"

namespace Envoy {
namespace Http {

Network::TransportSocketOptionsSharedPtr
wrapTransportSocketOptions(Network::TransportSocketOptionsSharedPtr transport_socket_options,
                           std::vector<Protocol> protocols) {
  std::vector<std::string> fallbacks;
  for (auto protocol : protocols) {
    // If configured to do so, we override the ALPN to use for the upstream connection to match the
    // selected protocol.
    switch (protocol) {
    case Http::Protocol::Http10:
      NOT_REACHED_GCOVR_EXCL_LINE;
    case Http::Protocol::Http11:
      fallbacks.push_back(Http::Utility::AlpnNames::get().Http11);
      break;
    case Http::Protocol::Http2:
      fallbacks.push_back(Http::Utility::AlpnNames::get().Http2);
      break;
    case Http::Protocol::Http3:
      // TODO(#14829) hard-code H3 ALPN, consider failing if other things are negotiated.
      break;
    }
  }

  if (transport_socket_options) {
    return std::make_shared<Network::AlpnDecoratingTransportSocketOptions>(
        std::move(fallbacks), transport_socket_options);
  } else {
    return std::make_shared<Network::TransportSocketOptionsImpl>(
        "", std::vector<std::string>{}, std::vector<std::string>{}, std::move(fallbacks));
  }
}

HttpConnPoolImplBase::HttpConnPoolImplBase(
    Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
    Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options,
    const Network::TransportSocketOptionsSharedPtr& transport_socket_options,
    Random::RandomGenerator& random_generator, Upstream::ClusterConnectivityState& state,
    std::vector<Http::Protocol> protocols)
    : Envoy::ConnectionPool::ConnPoolImplBase(
          host, priority, dispatcher, options,
          wrapTransportSocketOptions(transport_socket_options, protocols), state),
      random_generator_(random_generator) {
  ASSERT(!protocols.empty());
}

HttpConnPoolImplBase::~HttpConnPoolImplBase() { destructAllConnections(); }

ConnectionPool::Cancellable*
HttpConnPoolImplBase::newStream(Http::ResponseDecoder& response_decoder,
                                Http::ConnectionPool::Callbacks& callbacks) {
  HttpAttachContext context({&response_decoder, &callbacks});
  return Envoy::ConnectionPool::ConnPoolImplBase::newStream(context);
}

bool HttpConnPoolImplBase::hasActiveConnections() const {
  return (hasPendingStreams() || (hasActiveStreams()));
}

ConnectionPool::Cancellable*
HttpConnPoolImplBase::newPendingStream(Envoy::ConnectionPool::AttachContext& context) {
  Http::ResponseDecoder& decoder = *typedContext<HttpAttachContext>(context).decoder_;
  Http::ConnectionPool::Callbacks& callbacks = *typedContext<HttpAttachContext>(context).callbacks_;
  ENVOY_LOG(debug, "queueing stream due to no available connections");
  Envoy::ConnectionPool::PendingStreamPtr pending_stream(
      new HttpPendingStream(*this, decoder, callbacks));
  return addPendingStream(std::move(pending_stream));
}

void HttpConnPoolImplBase::onPoolReady(Envoy::ConnectionPool::ActiveClient& client,
                                       Envoy::ConnectionPool::AttachContext& context) {
  ActiveClient* http_client = static_cast<ActiveClient*>(&client);
  auto& http_context = typedContext<HttpAttachContext>(context);
  Http::ResponseDecoder& response_decoder = *http_context.decoder_;
  Http::ConnectionPool::Callbacks& callbacks = *http_context.callbacks_;
  Http::RequestEncoder& new_encoder = http_client->newStreamEncoder(response_decoder);
  callbacks.onPoolReady(new_encoder, client.real_host_description_,
                        http_client->codec_client_->streamInfo(),
                        http_client->codec_client_->protocol());
}

// All streams are 2^31. Client streams are half that, minus stream 0. Just to be on the safe
// side we do 2^29.
static const uint64_t DEFAULT_MAX_STREAMS = (1 << 29);

void MultiplexedActiveClientBase::onGoAway(Http::GoAwayErrorCode) {
  ENVOY_CONN_LOG(debug, "remote goaway", *codec_client_);
  parent_.host()->cluster().stats().upstream_cx_close_notify_.inc();
  if (state_ != ActiveClient::State::DRAINING) {
    if (codec_client_->numActiveRequests() == 0) {
      codec_client_->close();
    } else {
      parent_.transitionActiveClientState(*this, ActiveClient::State::DRAINING);
    }
  }
}

// Adjust the concurrent stream limit if the negotiated concurrent stream limit
// is lower than the local max configured streams.
//
// Note: if multiple streams are assigned to a connection before the settings
// are received, they may still be reset by the peer. This could be avoided by
// not considering http/2 connections connected until the SETTINGS frame is
// received, but that would result in a latency penalty instead.
void MultiplexedActiveClientBase::onSettings(ReceivedSettings& settings) {
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.improved_stream_limit_handling") &&
      settings.maxConcurrentStreams().has_value() &&
      settings.maxConcurrentStreams().value() < concurrent_stream_limit_) {
    int64_t old_unused_capacity = currentUnusedCapacity();
    // Given config limits old_unused_capacity should never exceed int32_t.
    // TODO(alyssawilk) move remaining_streams_, concurrent_stream_limit_ and
    // currentUnusedCapacity() to be explicit int32_t
    ASSERT(std::numeric_limits<int32_t>::max() >= old_unused_capacity);
    concurrent_stream_limit_ = settings.maxConcurrentStreams().value();
    int64_t delta = old_unused_capacity - currentUnusedCapacity();
    parent_.decrClusterStreamCapacity(delta);
    ENVOY_CONN_LOG(trace, "Decreasing stream capacity by {}", *codec_client_, delta);
    negative_capacity_ += delta;
  }
  // As we don't increase stream limits when maxConcurrentStreams goes up, treat
  // a stream limit of 0 as a GOAWAY.
  if (concurrent_stream_limit_ == 0) {
    parent_.transitionActiveClientState(*this, ActiveClient::State::DRAINING);
  }
}

void MultiplexedActiveClientBase::onStreamDestroy() {
  parent().onStreamClosed(*this, false);

  // If we are destroying this stream because of a disconnect, do not check for drain here. We will
  // wait until the connection has been fully drained of streams and then check in the connection
  // event callback.
  if (!closed_with_active_rq_) {
    parent().checkForDrained();
  }
}

void MultiplexedActiveClientBase::onStreamReset(Http::StreamResetReason reason) {
  switch (reason) {
  case StreamResetReason::ConnectionTermination:
  case StreamResetReason::ConnectionFailure:
    parent_.host()->cluster().stats().upstream_rq_pending_failure_eject_.inc();
    closed_with_active_rq_ = true;
    break;
  case StreamResetReason::LocalReset:
  case StreamResetReason::ProtocolError:
    parent_.host()->cluster().stats().upstream_rq_tx_reset_.inc();
    break;
  case StreamResetReason::RemoteReset:
    parent_.host()->cluster().stats().upstream_rq_rx_reset_.inc();
    break;
  case StreamResetReason::LocalRefusedStreamReset:
  case StreamResetReason::RemoteRefusedStreamReset:
  case StreamResetReason::Overflow:
  case StreamResetReason::ConnectError:
    break;
  }
}

uint64_t maxStreamsPerConnection(uint64_t max_streams_config) {
  return (max_streams_config != 0) ? max_streams_config : DEFAULT_MAX_STREAMS;
}

MultiplexedActiveClientBase::MultiplexedActiveClientBase(HttpConnPoolImplBase& parent,
                                                         uint32_t max_concurrent_streams,
                                                         Stats::Counter& cx_total)
    : Envoy::Http::ActiveClient(
          parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()),
          max_concurrent_streams) {
  codec_client_->setCodecClientCallbacks(*this);
  codec_client_->setCodecConnectionCallbacks(*this);
  cx_total.inc();
}

MultiplexedActiveClientBase::MultiplexedActiveClientBase(HttpConnPoolImplBase& parent,
                                                         uint32_t max_concurrent_streams,
                                                         Stats::Counter& cx_total,
                                                         Upstream::Host::CreateConnectionData& data)
    : Envoy::Http::ActiveClient(
          parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()),
          max_concurrent_streams, data) {
  codec_client_->setCodecClientCallbacks(*this);
  codec_client_->setCodecConnectionCallbacks(*this);
  cx_total.inc();
}

MultiplexedActiveClientBase::MultiplexedActiveClientBase(Envoy::Http::HttpConnPoolImplBase& parent,
                                                         Upstream::Host::CreateConnectionData& data,
                                                         uint32_t max_concurrent_streams,
                                                         Stats::Counter& cx_total)
    : Envoy::Http::ActiveClient(
          parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()),
          max_concurrent_streams, data) {
  codec_client_->setCodecClientCallbacks(*this);
  codec_client_->setCodecConnectionCallbacks(*this);
  cx_total.inc();
}

bool MultiplexedActiveClientBase::closingWithIncompleteStream() const {
  return closed_with_active_rq_;
}

RequestEncoder& MultiplexedActiveClientBase::newStreamEncoder(ResponseDecoder& response_decoder) {
  return codec_client_->newStream(response_decoder);
}

} // namespace Http
} // namespace Envoy
<commit_msg>quic: updating a TODO (#15600)<commit_after>#include "common/http/conn_pool_base.h"

#include "common/common/assert.h"
#include "common/http/utility.h"
#include "common/network/transport_socket_options_impl.h"
#include "common/runtime/runtime_features.h"
#include "common/stats/timespan_impl.h"
#include "common/upstream/upstream_impl.h"

namespace Envoy {
namespace Http {

Network::TransportSocketOptionsSharedPtr
wrapTransportSocketOptions(Network::TransportSocketOptionsSharedPtr transport_socket_options,
                           std::vector<Protocol> protocols) {
  std::vector<std::string> fallbacks;
  for (auto protocol : protocols) {
    // If configured to do so, we override the ALPN to use for the upstream connection to match the
    // selected protocol.
    switch (protocol) {
    case Http::Protocol::Http10:
      NOT_REACHED_GCOVR_EXCL_LINE;
    case Http::Protocol::Http11:
      fallbacks.push_back(Http::Utility::AlpnNames::get().Http11);
      break;
    case Http::Protocol::Http2:
      fallbacks.push_back(Http::Utility::AlpnNames::get().Http2);
      break;
    case Http::Protocol::Http3:
      // HTTP3 ALPN is set in the QUIC stack based on supported versions.
      break;
    }
  }

  if (transport_socket_options) {
    return std::make_shared<Network::AlpnDecoratingTransportSocketOptions>(
        std::move(fallbacks), transport_socket_options);
  } else {
    return std::make_shared<Network::TransportSocketOptionsImpl>(
        "", std::vector<std::string>{}, std::vector<std::string>{}, std::move(fallbacks));
  }
}

HttpConnPoolImplBase::HttpConnPoolImplBase(
    Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority,
    Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options,
    const Network::TransportSocketOptionsSharedPtr& transport_socket_options,
    Random::RandomGenerator& random_generator, Upstream::ClusterConnectivityState& state,
    std::vector<Http::Protocol> protocols)
    : Envoy::ConnectionPool::ConnPoolImplBase(
          host, priority, dispatcher, options,
          wrapTransportSocketOptions(transport_socket_options, protocols), state),
      random_generator_(random_generator) {
  ASSERT(!protocols.empty());
}

HttpConnPoolImplBase::~HttpConnPoolImplBase() { destructAllConnections(); }

ConnectionPool::Cancellable*
HttpConnPoolImplBase::newStream(Http::ResponseDecoder& response_decoder,
                                Http::ConnectionPool::Callbacks& callbacks) {
  HttpAttachContext context({&response_decoder, &callbacks});
  return Envoy::ConnectionPool::ConnPoolImplBase::newStream(context);
}

bool HttpConnPoolImplBase::hasActiveConnections() const {
  return (hasPendingStreams() || (hasActiveStreams()));
}

ConnectionPool::Cancellable*
HttpConnPoolImplBase::newPendingStream(Envoy::ConnectionPool::AttachContext& context) {
  Http::ResponseDecoder& decoder = *typedContext<HttpAttachContext>(context).decoder_;
  Http::ConnectionPool::Callbacks& callbacks = *typedContext<HttpAttachContext>(context).callbacks_;
  ENVOY_LOG(debug, "queueing stream due to no available connections");
  Envoy::ConnectionPool::PendingStreamPtr pending_stream(
      new HttpPendingStream(*this, decoder, callbacks));
  return addPendingStream(std::move(pending_stream));
}

void HttpConnPoolImplBase::onPoolReady(Envoy::ConnectionPool::ActiveClient& client,
                                       Envoy::ConnectionPool::AttachContext& context) {
  ActiveClient* http_client = static_cast<ActiveClient*>(&client);
  auto& http_context = typedContext<HttpAttachContext>(context);
  Http::ResponseDecoder& response_decoder = *http_context.decoder_;
  Http::ConnectionPool::Callbacks& callbacks = *http_context.callbacks_;
  Http::RequestEncoder& new_encoder = http_client->newStreamEncoder(response_decoder);
  callbacks.onPoolReady(new_encoder, client.real_host_description_,
                        http_client->codec_client_->streamInfo(),
                        http_client->codec_client_->protocol());
}

// All streams are 2^31. Client streams are half that, minus stream 0. Just to be on the safe
// side we do 2^29.
static const uint64_t DEFAULT_MAX_STREAMS = (1 << 29);

void MultiplexedActiveClientBase::onGoAway(Http::GoAwayErrorCode) {
  ENVOY_CONN_LOG(debug, "remote goaway", *codec_client_);
  parent_.host()->cluster().stats().upstream_cx_close_notify_.inc();
  if (state_ != ActiveClient::State::DRAINING) {
    if (codec_client_->numActiveRequests() == 0) {
      codec_client_->close();
    } else {
      parent_.transitionActiveClientState(*this, ActiveClient::State::DRAINING);
    }
  }
}

// Adjust the concurrent stream limit if the negotiated concurrent stream limit
// is lower than the local max configured streams.
//
// Note: if multiple streams are assigned to a connection before the settings
// are received, they may still be reset by the peer. This could be avoided by
// not considering http/2 connections connected until the SETTINGS frame is
// received, but that would result in a latency penalty instead.
void MultiplexedActiveClientBase::onSettings(ReceivedSettings& settings) {
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.improved_stream_limit_handling") &&
      settings.maxConcurrentStreams().has_value() &&
      settings.maxConcurrentStreams().value() < concurrent_stream_limit_) {
    int64_t old_unused_capacity = currentUnusedCapacity();
    // Given config limits old_unused_capacity should never exceed int32_t.
    // TODO(alyssawilk) move remaining_streams_, concurrent_stream_limit_ and
    // currentUnusedCapacity() to be explicit int32_t
    ASSERT(std::numeric_limits<int32_t>::max() >= old_unused_capacity);
    concurrent_stream_limit_ = settings.maxConcurrentStreams().value();
    int64_t delta = old_unused_capacity - currentUnusedCapacity();
    parent_.decrClusterStreamCapacity(delta);
    ENVOY_CONN_LOG(trace, "Decreasing stream capacity by {}", *codec_client_, delta);
    negative_capacity_ += delta;
  }
  // As we don't increase stream limits when maxConcurrentStreams goes up, treat
  // a stream limit of 0 as a GOAWAY.
  if (concurrent_stream_limit_ == 0) {
    parent_.transitionActiveClientState(*this, ActiveClient::State::DRAINING);
  }
}

void MultiplexedActiveClientBase::onStreamDestroy() {
  parent().onStreamClosed(*this, false);

  // If we are destroying this stream because of a disconnect, do not check for drain here. We will
  // wait until the connection has been fully drained of streams and then check in the connection
  // event callback.
  if (!closed_with_active_rq_) {
    parent().checkForDrained();
  }
}

void MultiplexedActiveClientBase::onStreamReset(Http::StreamResetReason reason) {
  switch (reason) {
  case StreamResetReason::ConnectionTermination:
  case StreamResetReason::ConnectionFailure:
    parent_.host()->cluster().stats().upstream_rq_pending_failure_eject_.inc();
    closed_with_active_rq_ = true;
    break;
  case StreamResetReason::LocalReset:
  case StreamResetReason::ProtocolError:
    parent_.host()->cluster().stats().upstream_rq_tx_reset_.inc();
    break;
  case StreamResetReason::RemoteReset:
    parent_.host()->cluster().stats().upstream_rq_rx_reset_.inc();
    break;
  case StreamResetReason::LocalRefusedStreamReset:
  case StreamResetReason::RemoteRefusedStreamReset:
  case StreamResetReason::Overflow:
  case StreamResetReason::ConnectError:
    break;
  }
}

uint64_t maxStreamsPerConnection(uint64_t max_streams_config) {
  return (max_streams_config != 0) ? max_streams_config : DEFAULT_MAX_STREAMS;
}

MultiplexedActiveClientBase::MultiplexedActiveClientBase(HttpConnPoolImplBase& parent,
                                                         uint32_t max_concurrent_streams,
                                                         Stats::Counter& cx_total)
    : Envoy::Http::ActiveClient(
          parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()),
          max_concurrent_streams) {
  codec_client_->setCodecClientCallbacks(*this);
  codec_client_->setCodecConnectionCallbacks(*this);
  cx_total.inc();
}

MultiplexedActiveClientBase::MultiplexedActiveClientBase(HttpConnPoolImplBase& parent,
                                                         uint32_t max_concurrent_streams,
                                                         Stats::Counter& cx_total,
                                                         Upstream::Host::CreateConnectionData& data)
    : Envoy::Http::ActiveClient(
          parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()),
          max_concurrent_streams, data) {
  codec_client_->setCodecClientCallbacks(*this);
  codec_client_->setCodecConnectionCallbacks(*this);
  cx_total.inc();
}

MultiplexedActiveClientBase::MultiplexedActiveClientBase(Envoy::Http::HttpConnPoolImplBase& parent,
                                                         Upstream::Host::CreateConnectionData& data,
                                                         uint32_t max_concurrent_streams,
                                                         Stats::Counter& cx_total)
    : Envoy::Http::ActiveClient(
          parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()),
          max_concurrent_streams, data) {
  codec_client_->setCodecClientCallbacks(*this);
  codec_client_->setCodecConnectionCallbacks(*this);
  cx_total.inc();
}

bool MultiplexedActiveClientBase::closingWithIncompleteStream() const {
  return closed_with_active_rq_;
}

RequestEncoder& MultiplexedActiveClientBase::newStreamEncoder(ResponseDecoder& response_decoder) {
  return codec_client_->newStream(response_decoder);
}

} // namespace Http
} // namespace Envoy
<|endoftext|>