<commit_before>#ifndef ITERBASE_HPP_
#define ITERBASE_HPP_

// This file consists of utilities used for the generic nature of the
// iterable wrapper classes.  As such, the contents of this file should be
// considered UNDOCUMENTED and is subject to change without warning.  This
// also applies to the name of the file.  No user code should include
// this file directly.

#include <utility>
#include <iterator>
#include <functional>
#include <memory>
#include <type_traits>
#include <cstddef>

namespace iter {

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using iterator_type =
        decltype(std::begin(std::declval<Container&>()));

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using iterator_deref =
        decltype(*std::declval<iterator_type<Container>&>());

    // const_iteator_deref is the type obtained through dereferencing
    // a const iterator& (note: not a const_iterator).  ie: the result
    // of Container::iterator::operator*() const
    template <typename Container>
    using const_iterator_deref =
        decltype(*std::declval<const iterator_type<Container>&>());

    template <typename Container>
    using iterator_traits_deref =
        typename std::remove_reference<iterator_deref<Container>>::type;

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using reverse_iterator_type =
        decltype(std::declval<Container&>().rbegin());

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using reverse_iterator_deref =
        decltype(*std::declval<reverse_iterator_type<Container>&>());

    template <typename, typename =void>
    struct is_random_access_iter : std::false_type { };

    template <typename T>
    struct is_random_access_iter<T,
        typename std::enable_if<
             std::is_same<
                 typename std::iterator_traits<T>::iterator_category,
              std::random_access_iterator_tag>::value,
          void>::type> : std::true_type { };

    template <typename T>
    using has_random_access_iter = is_random_access_iter<iterator_type<T>>;
    // because std::advance assumes a lot and is actually smart, I need a dumb

    // version that will work with most things
    template <typename InputIt, typename Distance =std::size_t>
    void dumb_advance(InputIt& iter, Distance distance=1) {
        for (Distance i(0); i < distance; ++i) {
            ++iter;
        }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(Iter& iter, const Iter& end,
            Distance distance, std::false_type) {
        for (Distance i(0); i < distance && iter != end; ++i) {
            ++iter;
        }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(Iter& iter, const Iter& end,
            Distance distance, std::true_type) {
        if (static_cast<Distance>(end - iter) < distance) {
            iter = end;
        } else {
            iter += distance;
        }
    }

    // iter will not be incremented past end
    template <typename Iter, typename Distance =std::size_t>
    void dumb_advance(Iter& iter, const Iter& end, Distance distance=1) {
        dumb_advance_impl(iter, end, distance, is_random_access_iter<Iter>{});
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(ForwardIt it, Distance distance=1) {
        dumb_advance(it, distance);
        return it;
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(
            ForwardIt it, const ForwardIt& end, Distance distance=1) {
        dumb_advance(it, end, distance);
        return it;
    }

    template <typename Container, typename Distance =std::size_t>
    Distance dumb_size(Container&& container) {
        Distance d{0};
        for (auto it = std::begin(container), end = std::end(container);
                it != end;
                ++it) {
            ++d;
        }
        return d;
    }

    template <typename... Ts>
    struct are_same : std::true_type { };

    template <typename T, typename U, typename... Ts>
    struct are_same<T, U, Ts...>
        : std::integral_constant<bool,
            std::is_same<T, U>::value && are_same<T, Ts...>::value> { };

    // DerefHolder holds the value gotten from an iterator dereference
    // if the iterate dereferences to an lvalue references, a pointer to the
    //     element is stored
    // if it does not, a value is stored instead
    // get() returns a reference to the held item in either case
    // pull() should be used when the item is being "pulled out" of the
    //     DerefHolder.  after pull() is called, neither it nor get() can be
    //     safely called after
    // reset() replaces the currently held item and may be called after pull()

    template <typename T, typename =void>
    class DerefHolder {
        private:
            static_assert(!std::is_lvalue_reference<T>::value,
                    "Non-lvalue-ref specialization used for lvalue ref type");
            // it could still be an rvalue reference
            using TPlain = typename std::remove_reference<T>::type;

            std::unique_ptr<TPlain> item_p;

        public:
            DerefHolder() = default;

            DerefHolder(const DerefHolder& other)
                : item_p{other.item_p ? new TPlain(*other.item_p) : nullptr}
            { }

            DerefHolder& operator=(const DerefHolder& other) {
                this->item_p.reset(other.item_p
                        ? new TPlain(*other.item_p) : nullptr);
                return *this;
            }

            DerefHolder(DerefHolder&&) = default;
            DerefHolder& operator=(DerefHolder&&) = default;
            ~DerefHolder() = default;

            TPlain& get() {
                return *item_p;
            }

            T pull() {
                // NOTE should I reset the unique_ptr to nullptr here
                // since its held item is now invalid anyway?
                return std::move(*item_p);
            }

            void reset(T&& item) {
                item_p.reset(new TPlain(std::move(item)));
            }

            explicit operator bool() const {
                return this->item_p;
            }
    };

    // Specialization for when T is an lvalue ref.  Keep this in mind
    // wherever a T appears.
    template <typename T>
    class DerefHolder<T,
        typename std::enable_if<std::is_lvalue_reference<T>::value>::type>
    {
        private:
            static_assert(std::is_lvalue_reference<T>::value,
                    "lvalue specialization handling non-lvalue-ref type");

            typename std::remove_reference<T>::type *item_p =nullptr;
        public:
            DerefHolder() = default;

            T get() {
                return *this->item_p;
            }

            T pull() {
                return this->get();
            }

            void reset(T item) {
                this->item_p = &item;
            }

            explicit operator bool() const {
                return this->item_p != nullptr;
            }
    };

    template <typename T>
    struct type_is {
        using type = T;
    };

    // gcc CWG 1558
    template <typename...>
    struct void_t_help {
        using type = void;
    };
    template <typename... Ts>
    using void_t = typename void_t_help<Ts...>::type;

}

#endif
<commit_msg>DerefHolder pull() behavior removed<commit_after>#ifndef ITERBASE_HPP_
#define ITERBASE_HPP_

// This file consists of utilities used for the generic nature of the
// iterable wrapper classes.  As such, the contents of this file should be
// considered UNDOCUMENTED and is subject to change without warning.  This
// also applies to the name of the file.  No user code should include
// this file directly.

#include <utility>
#include <iterator>
#include <functional>
#include <memory>
#include <type_traits>
#include <cstddef>

namespace iter {

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using iterator_type =
        decltype(std::begin(std::declval<Container&>()));

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using iterator_deref =
        decltype(*std::declval<iterator_type<Container>&>());

    // const_iteator_deref is the type obtained through dereferencing
    // a const iterator& (note: not a const_iterator).  ie: the result
    // of Container::iterator::operator*() const
    template <typename Container>
    using const_iterator_deref =
        decltype(*std::declval<const iterator_type<Container>&>());

    template <typename Container>
    using iterator_traits_deref =
        typename std::remove_reference<iterator_deref<Container>>::type;

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using reverse_iterator_type =
        decltype(std::declval<Container&>().rbegin());

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using reverse_iterator_deref =
        decltype(*std::declval<reverse_iterator_type<Container>&>());

    template <typename, typename =void>
    struct is_random_access_iter : std::false_type { };

    template <typename T>
    struct is_random_access_iter<T,
        typename std::enable_if<
             std::is_same<
                 typename std::iterator_traits<T>::iterator_category,
              std::random_access_iterator_tag>::value,
          void>::type> : std::true_type { };

    template <typename T>
    using has_random_access_iter = is_random_access_iter<iterator_type<T>>;
    // because std::advance assumes a lot and is actually smart, I need a dumb

    // version that will work with most things
    template <typename InputIt, typename Distance =std::size_t>
    void dumb_advance(InputIt& iter, Distance distance=1) {
        for (Distance i(0); i < distance; ++i) {
            ++iter;
        }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(Iter& iter, const Iter& end,
            Distance distance, std::false_type) {
        for (Distance i(0); i < distance && iter != end; ++i) {
            ++iter;
        }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(Iter& iter, const Iter& end,
            Distance distance, std::true_type) {
        if (static_cast<Distance>(end - iter) < distance) {
            iter = end;
        } else {
            iter += distance;
        }
    }

    // iter will not be incremented past end
    template <typename Iter, typename Distance =std::size_t>
    void dumb_advance(Iter& iter, const Iter& end, Distance distance=1) {
        dumb_advance_impl(iter, end, distance, is_random_access_iter<Iter>{});
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(ForwardIt it, Distance distance=1) {
        dumb_advance(it, distance);
        return it;
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(
            ForwardIt it, const ForwardIt& end, Distance distance=1) {
        dumb_advance(it, end, distance);
        return it;
    }

    template <typename Container, typename Distance =std::size_t>
    Distance dumb_size(Container&& container) {
        Distance d{0};
        for (auto it = std::begin(container), end = std::end(container);
                it != end;
                ++it) {
            ++d;
        }
        return d;
    }

    template <typename... Ts>
    struct are_same : std::true_type { };

    template <typename T, typename U, typename... Ts>
    struct are_same<T, U, Ts...>
        : std::integral_constant<bool,
            std::is_same<T, U>::value && are_same<T, Ts...>::value> { };

    // DerefHolder holds the value gotten from an iterator dereference
    // if the iterate dereferences to an lvalue references, a pointer to the
    //     element is stored
    // if it does not, a value is stored instead
    // get() returns a reference to the held item in either case
    // pull() should be used when the item is being "pulled out" of the
    //     DerefHolder.  after pull() is called, neither it nor get() can be
    //     safely called after
    // reset() replaces the currently held item and may be called after pull()

    template <typename T, typename =void>
    class DerefHolder {
        private:
            static_assert(!std::is_lvalue_reference<T>::value,
                    "Non-lvalue-ref specialization used for lvalue ref type");
            // it could still be an rvalue reference
            using TPlain = typename std::remove_reference<T>::type;

            std::unique_ptr<TPlain> item_p;

        public:
            using reference = TPlain&;
            using pointer = TPlain*;

            DerefHolder() = default;

            DerefHolder(const DerefHolder& other)
                : item_p{other.item_p ? new TPlain(*other.item_p) : nullptr}
            { }

            DerefHolder& operator=(const DerefHolder& other) {
                this->item_p.reset(other.item_p
                        ? new TPlain(*other.item_p) : nullptr);
                return *this;
            }

            DerefHolder(DerefHolder&&) = default;
            DerefHolder& operator=(DerefHolder&&) = default;
            ~DerefHolder() = default;

            reference get() {
                return *this->item_p;
            }

            pointer get_ptr() {
                return this->item_p.get();
            }

            void reset(T&& item) {
                item_p.reset(new TPlain(std::move(item)));
            }

            explicit operator bool() const {
                return this->item_p;
            }
    };

    // Specialization for when T is an lvalue ref.  Keep this in mind
    // wherever a T appears.
    template <typename T>
    class DerefHolder<T,
        typename std::enable_if<std::is_lvalue_reference<T>::value>::type>
    {
        public:
            using reference = T;
            using pointer = typename std::remove_reference<T>::type*;

        private:
            pointer item_p{};

        public:
            DerefHolder() = default;

            reference get() {
                return *this->item_p;
            }

            pointer get_ptr() {
                return this->item_p;
            }

            void reset(T item) {
                this->item_p = &item;
            }

            explicit operator bool() const {
                return this->item_p != nullptr;
            }
    };

    template <typename T>
    struct type_is {
        using type = T;
    };

    // gcc CWG 1558
    template <typename...>
    struct void_t_help {
        using type = void;
    };
    template <typename... Ts>
    using void_t = typename void_t_help<Ts...>::type;

}

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#ifndef DELETER_HH_
#define DELETER_HH_

#include <memory>

class deleter {
public:
    struct impl;
private:
    impl* _impl = nullptr;
public:
    deleter() = default;
    deleter(const deleter&) = delete;
    deleter(deleter&& x) : _impl(x._impl) { x._impl = nullptr; }
    explicit deleter(impl* i) : _impl(i) {}
    ~deleter();
    deleter& operator=(deleter&& x);
    deleter& operator=(deleter&) = delete;
    impl& operator*() const { return *_impl; }
    impl* operator->() const { return _impl; }
    impl* get() const { return _impl; }
    void unshare();
    deleter share();
    explicit operator bool() const { return bool(_impl); }
    void reset(impl* i) {
        this->~deleter();
        new (this) deleter(i);
    }
};

struct deleter::impl {
    unsigned refs = 1;
    deleter next;
    impl(deleter next) : next(std::move(next)) {}
    virtual ~impl() {}
};

inline
deleter::~deleter() {
    if (_impl && --_impl->refs == 0) {
        delete _impl;
    }
}

inline
deleter& deleter::operator=(deleter&& x) {
    if (this != &x) {
        this->~deleter();
        new (this) deleter(std::move(x));
    }
    return *this;
}

template <typename Deleter>
struct lambda_deleter_impl final : deleter::impl {
    Deleter del;
    lambda_deleter_impl(deleter next, Deleter&& del)
        : impl(std::move(next)), del(std::move(del)) {}
    virtual ~lambda_deleter_impl() override { del(); }
};

template <typename Deleter>
deleter
make_deleter(deleter next, Deleter d) {
    return deleter(new lambda_deleter_impl<Deleter>(std::move(next), std::move(d)));
}

inline
deleter
deleter::share() {
    if (!_impl) {
        return deleter();
    }
    ++_impl->refs;
    return deleter(_impl);
}

#endif /* DELETER_HH_ */
<commit_msg>core: special-case deleter for raw memory<commit_after>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#ifndef DELETER_HH_
#define DELETER_HH_

#include <memory>
#include <cstdlib>
#include <type_traits>

class deleter {
public:
    struct impl;
    struct raw_object_tag {};
private:
    // if bit 0 set, point to object to be freed directly.
    impl* _impl = nullptr;
public:
    deleter() = default;
    deleter(const deleter&) = delete;
    deleter(deleter&& x) : _impl(x._impl) { x._impl = nullptr; }
    explicit deleter(impl* i) : _impl(i) {}
    deleter(raw_object_tag tag, void* object)
        : _impl(from_raw_object(object)) {}
    ~deleter();
    deleter& operator=(deleter&& x);
    deleter& operator=(deleter&) = delete;
    impl& operator*() const { return *_impl; }
    impl* operator->() const { return _impl; }
    impl* get() const { return _impl; }
    void unshare();
    deleter share();
    explicit operator bool() const { return bool(_impl); }
    void reset(impl* i) {
        this->~deleter();
        new (this) deleter(i);
    }
private:
    bool is_raw_object() const {
        auto x = reinterpret_cast<uintptr_t>(_impl);
        return x & 1;
    }
    void* to_raw_object() const {
        auto x = reinterpret_cast<uintptr_t>(_impl);
        return reinterpret_cast<void*>(x & ~uintptr_t(1));
    }
    impl* from_raw_object(void* object) {
        auto x = reinterpret_cast<uintptr_t>(object);
        return reinterpret_cast<impl*>(x | 1);
    }
};

struct deleter::impl {
    unsigned refs = 1;
    deleter next;
    impl(deleter next) : next(std::move(next)) {}
    virtual ~impl() {}
};

inline
deleter::~deleter() {
    if (is_raw_object()) {
        std::free(to_raw_object());
        return;
    }
    if (_impl && --_impl->refs == 0) {
        delete _impl;
    }
}

inline
deleter& deleter::operator=(deleter&& x) {
    if (this != &x) {
        this->~deleter();
        new (this) deleter(std::move(x));
    }
    return *this;
}

template <typename Deleter>
struct lambda_deleter_impl final : deleter::impl {
    Deleter del;
    lambda_deleter_impl(deleter next, Deleter&& del)
        : impl(std::move(next)), del(std::move(del)) {}
    virtual ~lambda_deleter_impl() override { del(); }
};

template <typename Deleter>
deleter
make_deleter(deleter next, Deleter d) {
    return deleter(new lambda_deleter_impl<Deleter>(std::move(next), std::move(d)));
}

struct free_deleter_impl final : deleter::impl {
    void* obj;
    free_deleter_impl(void* obj) : impl(deleter()), obj(obj) {}
    virtual ~free_deleter_impl() override { std::free(obj); }
};

inline
deleter
deleter::share() {
    if (!_impl) {
        return deleter();
    }
    if (is_raw_object()) {
        _impl = new free_deleter_impl(to_raw_object());
    }
    ++_impl->refs;
    return deleter(_impl);
}

inline
deleter
make_free_deleter(void* obj) {
    return deleter(deleter::raw_object_tag(), obj);
}

#endif /* DELETER_HH_ */
<|endoftext|>


<commit_before>/* 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 http://mozilla.org/MPL/2.0/. */

#include "stdafx.h"
#include "sn_utils.h"

// sanity checks
static_assert(std::numeric_limits<double>::is_iec559, "IEEE754 required");
static_assert(sizeof(float) == 4, "Float must be 4 bytes");
static_assert(sizeof(double) == 8, "Double must be 8 bytes");
static_assert(-1 == ~0, "Two's complement required");

// deserialize little endian uint16
uint16_t sn_utils::ld_uint16(std::istream &s)
{
	uint8_t buf[2];
	s.read((char*)buf, 2);
	uint16_t v = (buf[1] << 8) | buf[0];
	return reinterpret_cast<uint16_t&>(v);
}

// deserialize little endian uint32
uint32_t sn_utils::ld_uint32(std::istream &s)
{
	uint8_t buf[4];
	s.read((char*)buf, 4);
	uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
	return reinterpret_cast<uint32_t&>(v);
}

// deserialize little endian int32
int32_t sn_utils::ld_int32(std::istream &s)
{
	uint8_t buf[4];
	s.read((char*)buf, 4);
	uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
	return reinterpret_cast<int32_t&>(v);
}

// deserialize little endian uint64
uint64_t sn_utils::ld_uint64(std::istream &s)
{
	uint8_t buf[8];
	s.read((char*)buf, 8);
	uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
	             ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	             ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
	             ((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
	return reinterpret_cast<uint64_t&>(v);
}

// deserialize little endian int64
int64_t sn_utils::ld_int64(std::istream &s)
{
	uint8_t buf[8];
	s.read((char*)buf, 8);
	uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
	             ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	             ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
	             ((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
	return reinterpret_cast<int64_t&>(v);
}

// deserialize little endian ieee754 float32
float sn_utils::ld_float32(std::istream &s)
{
	uint8_t buf[4];
	s.read((char*)buf, 4);
	uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
	return reinterpret_cast<float&>(v);
}

// deserialize little endian ieee754 float64
double sn_utils::ld_float64(std::istream &s)
{
	uint8_t buf[8];
	s.read((char*)buf, 8);
	uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
		         ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	             ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
		         ((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
	return reinterpret_cast<double&>(v);
}

// deserialize null-terminated string
std::string sn_utils::d_str(std::istream &s)
{
	std::string r;
	r.reserve(32);
	char buf[1];
	while (true)
	{
		s.read(buf, 1);
		if (buf[0] == 0)
			break;
		r.push_back(buf[0]);
	}
	return r;
}

bool sn_utils::d_bool(std::istream& s)
{
    return ( ld_uint16( s ) == 1 );
}

glm::dvec3 sn_utils::d_dvec3(std::istream& s)
{
    return {
        ld_float64(s),
        ld_float64(s),
        ld_float64(s) };
}

glm::vec3 sn_utils::d_vec3( std::istream& s )
{
	return {
		ld_float32(s),
		ld_float32(s),
		ld_float32(s) };
}

glm::vec4 sn_utils::d_vec4( std::istream& s )
{
    return {
        ld_float32(s),
        ld_float32(s),
        ld_float32(s),
        ld_float32(s) };
}

uint8_t sn_utils::d_uint8( std::istream& s ) {

	uint8_t buf;
	s.read((char*)buf, 1);
	return buf;
}

void sn_utils::ls_uint16(std::ostream &s, uint16_t v)
{
	uint8_t buf[2];
	buf[0] = v;
	buf[1] = v >> 8;
	s.write((char*)buf, 2);
}

void sn_utils::ls_uint32(std::ostream &s, uint32_t v)
{
	uint8_t buf[4];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	s.write((char*)buf, 4);
}

void sn_utils::ls_int32(std::ostream &s, int32_t v)
{
	uint8_t buf[4];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	s.write((char*)buf, 4);
}

void sn_utils::ls_uint64(std::ostream &s, uint64_t v)
{
	uint8_t buf[8];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	buf[4] = v >> 32;
	buf[5] = v >> 40;
	buf[6] = v >> 48;
	buf[7] = v >> 56;
	s.write((char*)buf, 8);
}

void sn_utils::ls_int64(std::ostream &s, int64_t v)
{
	uint8_t buf[8];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	buf[4] = v >> 32;
	buf[5] = v >> 40;
	buf[6] = v >> 48;
	buf[7] = v >> 56;
	s.write((char*)buf, 8);
}

void sn_utils::ls_float32(std::ostream &s, float t)
{
	uint32_t v = reinterpret_cast<uint32_t&>(t);
	uint8_t buf[4];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	s.write((char*)buf, 4);
}

void sn_utils::ls_float64(std::ostream &s, double t)
{
	uint64_t v = reinterpret_cast<uint64_t&>(t);
	uint8_t buf[8];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	buf[4] = v >> 32;
	buf[5] = v >> 40;
	buf[6] = v >> 48;
	buf[7] = v >> 56;
	s.write((char*)buf, 8);
}

void sn_utils::s_uint8(std::ostream &s, uint8_t v)
{
    s.write((char*)v, 1);
}

void sn_utils::s_str(std::ostream &s, std::string v)
{
	const char* buf = v.c_str();
	s.write(buf, v.size() + 1);
}

void sn_utils::s_bool(std::ostream &s, bool v)
{
    ls_uint16(
        s,
        ( true == v ?
            1 :
            0 ) );
}

void sn_utils::s_dvec3(std::ostream &s, glm::dvec3 const &v)
{
    ls_float64(s, v.x);
    ls_float64(s, v.y);
    ls_float64(s, v.z);
}

void sn_utils::s_vec3(std::ostream &s, glm::vec3 const &v)
{
	ls_float32(s, v.x);
	ls_float32(s, v.y);
	ls_float32(s, v.z);
}

void sn_utils::s_vec4(std::ostream &s, glm::vec4 const &v)
{
    ls_float32(s, v.x);
    ls_float32(s, v.y);
    ls_float32(s, v.z);
    ls_float32(s, v.w);
}
<commit_msg>fix uint8 serialization<commit_after>/* 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 http://mozilla.org/MPL/2.0/. */

#include "stdafx.h"
#include "sn_utils.h"

// sanity checks
static_assert(std::numeric_limits<double>::is_iec559, "IEEE754 required");
static_assert(sizeof(float) == 4, "Float must be 4 bytes");
static_assert(sizeof(double) == 8, "Double must be 8 bytes");
static_assert(-1 == ~0, "Two's complement required");

// deserialize little endian uint16
uint16_t sn_utils::ld_uint16(std::istream &s)
{
	uint8_t buf[2];
	s.read((char*)buf, 2);
	uint16_t v = (buf[1] << 8) | buf[0];
	return reinterpret_cast<uint16_t&>(v);
}

// deserialize little endian uint32
uint32_t sn_utils::ld_uint32(std::istream &s)
{
	uint8_t buf[4];
	s.read((char*)buf, 4);
	uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
	return reinterpret_cast<uint32_t&>(v);
}

// deserialize little endian int32
int32_t sn_utils::ld_int32(std::istream &s)
{
	uint8_t buf[4];
	s.read((char*)buf, 4);
	uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
	return reinterpret_cast<int32_t&>(v);
}

// deserialize little endian uint64
uint64_t sn_utils::ld_uint64(std::istream &s)
{
	uint8_t buf[8];
	s.read((char*)buf, 8);
	uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
	             ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	             ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
	             ((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
	return reinterpret_cast<uint64_t&>(v);
}

// deserialize little endian int64
int64_t sn_utils::ld_int64(std::istream &s)
{
	uint8_t buf[8];
	s.read((char*)buf, 8);
	uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
	             ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	             ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
	             ((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
	return reinterpret_cast<int64_t&>(v);
}

// deserialize little endian ieee754 float32
float sn_utils::ld_float32(std::istream &s)
{
	uint8_t buf[4];
	s.read((char*)buf, 4);
	uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
	return reinterpret_cast<float&>(v);
}

// deserialize little endian ieee754 float64
double sn_utils::ld_float64(std::istream &s)
{
	uint8_t buf[8];
	s.read((char*)buf, 8);
	uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
		         ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	             ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
		         ((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
	return reinterpret_cast<double&>(v);
}

// deserialize null-terminated string
std::string sn_utils::d_str(std::istream &s)
{
	std::string r;
	r.reserve(32);
	char buf[1];
	while (true)
	{
		s.read(buf, 1);
		if (buf[0] == 0)
			break;
		r.push_back(buf[0]);
	}
	return r;
}

bool sn_utils::d_bool(std::istream& s)
{
    return ( ld_uint16( s ) == 1 );
}

glm::dvec3 sn_utils::d_dvec3(std::istream& s)
{
    return {
        ld_float64(s),
        ld_float64(s),
        ld_float64(s) };
}

glm::vec3 sn_utils::d_vec3( std::istream& s )
{
	return {
		ld_float32(s),
		ld_float32(s),
		ld_float32(s) };
}

glm::vec4 sn_utils::d_vec4( std::istream& s )
{
    return {
        ld_float32(s),
        ld_float32(s),
        ld_float32(s),
        ld_float32(s) };
}

uint8_t sn_utils::d_uint8( std::istream& s ) {

	uint8_t buf;
    s.read((char*)&buf, 1);
	return buf;
}

void sn_utils::ls_uint16(std::ostream &s, uint16_t v)
{
	uint8_t buf[2];
	buf[0] = v;
	buf[1] = v >> 8;
	s.write((char*)buf, 2);
}

void sn_utils::ls_uint32(std::ostream &s, uint32_t v)
{
	uint8_t buf[4];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	s.write((char*)buf, 4);
}

void sn_utils::ls_int32(std::ostream &s, int32_t v)
{
	uint8_t buf[4];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	s.write((char*)buf, 4);
}

void sn_utils::ls_uint64(std::ostream &s, uint64_t v)
{
	uint8_t buf[8];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	buf[4] = v >> 32;
	buf[5] = v >> 40;
	buf[6] = v >> 48;
	buf[7] = v >> 56;
	s.write((char*)buf, 8);
}

void sn_utils::ls_int64(std::ostream &s, int64_t v)
{
	uint8_t buf[8];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	buf[4] = v >> 32;
	buf[5] = v >> 40;
	buf[6] = v >> 48;
	buf[7] = v >> 56;
	s.write((char*)buf, 8);
}

void sn_utils::ls_float32(std::ostream &s, float t)
{
	uint32_t v = reinterpret_cast<uint32_t&>(t);
	uint8_t buf[4];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	s.write((char*)buf, 4);
}

void sn_utils::ls_float64(std::ostream &s, double t)
{
	uint64_t v = reinterpret_cast<uint64_t&>(t);
	uint8_t buf[8];
	buf[0] = v;
	buf[1] = v >> 8;
	buf[2] = v >> 16;
	buf[3] = v >> 24;
	buf[4] = v >> 32;
	buf[5] = v >> 40;
	buf[6] = v >> 48;
	buf[7] = v >> 56;
	s.write((char*)buf, 8);
}

void sn_utils::s_uint8(std::ostream &s, uint8_t v)
{
    s.write((char*)&v, 1);
}

void sn_utils::s_str(std::ostream &s, std::string v)
{
	const char* buf = v.c_str();
	s.write(buf, v.size() + 1);
}

void sn_utils::s_bool(std::ostream &s, bool v)
{
    ls_uint16(
        s,
        ( true == v ?
            1 :
            0 ) );
}

void sn_utils::s_dvec3(std::ostream &s, glm::dvec3 const &v)
{
    ls_float64(s, v.x);
    ls_float64(s, v.y);
    ls_float64(s, v.z);
}

void sn_utils::s_vec3(std::ostream &s, glm::vec3 const &v)
{
	ls_float32(s, v.x);
	ls_float32(s, v.y);
	ls_float32(s, v.z);
}

void sn_utils::s_vec4(std::ostream &s, glm::vec4 const &v)
{
    ls_float32(s, v.x);
    ls_float32(s, v.y);
    ls_float32(s, v.z);
    ls_float32(s, v.w);
}
<|endoftext|>


<commit_before>/* Copyright 2019 Google LLC. All Rights Reserved.

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.
==============================================================================*/

#include "ruy/allocator.h"

#include "ruy/opt_set.h"
#include "ruy/size_util.h"
#include "ruy/system_aligned_alloc.h"

namespace ruy {

Allocator::~Allocator() {
  FreeAll();
  detail::SystemAlignedFree(ptr_);
}

void* Allocator::AllocateFast(std::ptrdiff_t num_bytes) {
  if (current_ + num_bytes > size_) {
    return nullptr;
  }
  void* ret = static_cast<char*>(ptr_) + current_;
  current_ += num_bytes;
  return ret;
}

void* Allocator::AllocateSlow(std::ptrdiff_t num_bytes) {
  void* p = detail::SystemAlignedAlloc(num_bytes);
  fallback_blocks_total_size_ += num_bytes;
  fallback_blocks_.push_back(p);
  return p;
}

void* Allocator::AllocateBytes(std::ptrdiff_t num_bytes) {
  if (num_bytes == 0) {
    return nullptr;
  }
  const std::ptrdiff_t rounded_num_bytes =
      round_up_pot(num_bytes, detail::kMinimumBlockAlignment);
  if (void* p = AllocateFast(rounded_num_bytes)) {
    return p;
  }
  return AllocateSlow(rounded_num_bytes);
}

void* Allocator::AllocateBytesAvoidingAliasingWith(std::ptrdiff_t num_bytes,
                                                   const void* to_avoid) {
#if RUY_OPT(AVOID_ALIASING)
  if (num_bytes == 0) {
    return nullptr;
  }
  // The minimum L1D cache aliasing periodicity in bytes that we expect to
  // encounter on any device. This does not seem to be documented, but
  // empirically we observe the following:
  //   Cortex-A53:   1024
  //   Cortex-A55r1: 2048
  //   Cortex-A76:   not as easily observable.
  // Over-estimating this makes the AVOID_ALIASING optimization useless on
  // devices with lower periodicity.
  // Under-estimating this by 2x should be harmless.
  // Under-estimating this by a larger factor should gradually degrade
  // performance due to cache aliasing causing mutual eviction between
  // the packed matrix data, and the source matrix data being prefetched by the
  // CPU ahead of the packing code execution.
  static constexpr std::uint32_t kMinPeriod = 1024;
  static_assert(is_pot(kMinPeriod), "");
  void* p = AllocateBytes(num_bytes + kMinPeriod);
  auto unsigned_low_bits = [](const void* p) {
    return static_cast<std::uint32_t>(reinterpret_cast<std::uintptr_t>(p));
  };
  // This relies on unsigned integer overflow wrapping around.
  std::uint32_t diff_modulus =
      (unsigned_low_bits(p) - unsigned_low_bits(to_avoid)) % kMinPeriod;
  // diff_modulus is in [0, kMinPeriod).
  // We want it as close as possible to the middle of that interval,
  // kMinPeriod/2. The bad 'aliasing' case, that we are working to avoid,
  // is when diff_modulus is close to the ends of that interval, 0 or
  // kMinPeriod. So we want to add an offset of kMinPeriod/2 if it is in the
  // first or the last quarter of that interval.
  bool need_offset =
      diff_modulus < kMinPeriod / 4 || diff_modulus > 3 * kMinPeriod / 4;
  return static_cast<char*>(p) + (need_offset ? (kMinPeriod / 2) : 0);
#else
  (void)to_avoid;
  return AllocateBytes(num_bytes);
#endif
}

void Allocator::FreeAll() {
  current_ = 0;
  if (fallback_blocks_.empty()) {
    return;
  }

  // No rounding-up of the size means linear instead of logarithmic
  // bound on the number of allocation in some worst-case calling patterns.
  // This is considered worth it because minimizing memory usage is important
  // and actual calling patterns in applications that we care about still
  // reach the no-further-allocations steady state in a small finite number
  // of iterations.
  std::ptrdiff_t new_size = size_ + fallback_blocks_total_size_;
  detail::SystemAlignedFree(ptr_);
  ptr_ = detail::SystemAlignedAlloc(new_size);
  size_ = new_size;

  for (void* p : fallback_blocks_) {
    detail::SystemAlignedFree(p);
  }
  fallback_blocks_.clear();
  fallback_blocks_total_size_ = 0;
}

}  // namespace ruy
<commit_msg>Free all memory before reallocating.<commit_after>/* Copyright 2019 Google LLC. All Rights Reserved.

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.
==============================================================================*/

#include "ruy/allocator.h"

#include "ruy/opt_set.h"
#include "ruy/size_util.h"
#include "ruy/system_aligned_alloc.h"

namespace ruy {

Allocator::~Allocator() {
  FreeAll();
  detail::SystemAlignedFree(ptr_);
}

void* Allocator::AllocateFast(std::ptrdiff_t num_bytes) {
  if (current_ + num_bytes > size_) {
    return nullptr;
  }
  void* ret = static_cast<char*>(ptr_) + current_;
  current_ += num_bytes;
  return ret;
}

void* Allocator::AllocateSlow(std::ptrdiff_t num_bytes) {
  void* p = detail::SystemAlignedAlloc(num_bytes);
  fallback_blocks_total_size_ += num_bytes;
  fallback_blocks_.push_back(p);
  return p;
}

void* Allocator::AllocateBytes(std::ptrdiff_t num_bytes) {
  if (num_bytes == 0) {
    return nullptr;
  }
  const std::ptrdiff_t rounded_num_bytes =
      round_up_pot(num_bytes, detail::kMinimumBlockAlignment);
  if (void* p = AllocateFast(rounded_num_bytes)) {
    return p;
  }
  return AllocateSlow(rounded_num_bytes);
}

void* Allocator::AllocateBytesAvoidingAliasingWith(std::ptrdiff_t num_bytes,
                                                   const void* to_avoid) {
#if RUY_OPT(AVOID_ALIASING)
  if (num_bytes == 0) {
    return nullptr;
  }
  // The minimum L1D cache aliasing periodicity in bytes that we expect to
  // encounter on any device. This does not seem to be documented, but
  // empirically we observe the following:
  //   Cortex-A53:   1024
  //   Cortex-A55r1: 2048
  //   Cortex-A76:   not as easily observable.
  // Over-estimating this makes the AVOID_ALIASING optimization useless on
  // devices with lower periodicity.
  // Under-estimating this by 2x should be harmless.
  // Under-estimating this by a larger factor should gradually degrade
  // performance due to cache aliasing causing mutual eviction between
  // the packed matrix data, and the source matrix data being prefetched by the
  // CPU ahead of the packing code execution.
  static constexpr std::uint32_t kMinPeriod = 1024;
  static_assert(is_pot(kMinPeriod), "");
  void* p = AllocateBytes(num_bytes + kMinPeriod);
  auto unsigned_low_bits = [](const void* p) {
    return static_cast<std::uint32_t>(reinterpret_cast<std::uintptr_t>(p));
  };
  // This relies on unsigned integer overflow wrapping around.
  std::uint32_t diff_modulus =
      (unsigned_low_bits(p) - unsigned_low_bits(to_avoid)) % kMinPeriod;
  // diff_modulus is in [0, kMinPeriod).
  // We want it as close as possible to the middle of that interval,
  // kMinPeriod/2. The bad 'aliasing' case, that we are working to avoid,
  // is when diff_modulus is close to the ends of that interval, 0 or
  // kMinPeriod. So we want to add an offset of kMinPeriod/2 if it is in the
  // first or the last quarter of that interval.
  bool need_offset =
      diff_modulus < kMinPeriod / 4 || diff_modulus > 3 * kMinPeriod / 4;
  return static_cast<char*>(p) + (need_offset ? (kMinPeriod / 2) : 0);
#else
  (void)to_avoid;
  return AllocateBytes(num_bytes);
#endif
}

void Allocator::FreeAll() {
  current_ = 0;
  if (fallback_blocks_.empty()) {
    return;
  }

  // Free all memory before reallocating `ptr_`.
  // This minimizes the memory high-water-mark.
  detail::SystemAlignedFree(ptr_);
  for (void* p : fallback_blocks_) {
    detail::SystemAlignedFree(p);
  }

  // We reallocate to the exact new size, rather than growing
  // exponentially like std::vector. This means linear instead of logarithmic
  // bound on the number of allocation in some worst-case calling patterns.
  // This is considered worth it because minimizing memory usage is important
  // and actual calling patterns in applications that we care about still
  // reach the no-further-allocations steady state in a small finite number
  // of iterations.
  std::ptrdiff_t new_size = size_ + fallback_blocks_total_size_;
  ptr_ = detail::SystemAlignedAlloc(new_size);
  size_ = new_size;

  fallback_blocks_.clear();
  fallback_blocks_total_size_ = 0;
}

}  // namespace ruy
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sync/sessions/sync_session.h"

#include <algorithm>

#include "base/logging.h"
#include "sync/syncable/model_type.h"
#include "sync/syncable/syncable.h"

namespace browser_sync {
namespace sessions {

namespace {

std::set<ModelSafeGroup> ComputeEnabledGroups(
    const ModelSafeRoutingInfo& routing_info,
    const std::vector<ModelSafeWorker*>& workers) {
  std::set<ModelSafeGroup> enabled_groups;
  // Project the list of enabled types (i.e., types in the routing
  // info) to a list of enabled groups.
  for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin();
       it != routing_info.end(); ++it) {
    enabled_groups.insert(it->second);
  }
  // GROUP_PASSIVE is always enabled, since that's the group that
  // top-level folders map to.
  enabled_groups.insert(GROUP_PASSIVE);
  if (DCHECK_IS_ON()) {
    // We sometimes create dummy SyncSession objects (see
    // SyncScheduler::InitialSnapshot) so don't check in that case.
    if (!routing_info.empty() || !workers.empty()) {
      std::set<ModelSafeGroup> groups_with_workers;
      for (std::vector<ModelSafeWorker*>::const_iterator it = workers.begin();
           it != workers.end(); ++it) {
        groups_with_workers.insert((*it)->GetModelSafeGroup());
      }
      // All enabled groups should have a corresponding worker.
      DCHECK(std::includes(
          groups_with_workers.begin(), groups_with_workers.end(),
          enabled_groups.begin(), enabled_groups.end()));
    }
  }
  return enabled_groups;
}

}  // namesepace

SyncSession::SyncSession(SyncSessionContext* context, Delegate* delegate,
    const SyncSourceInfo& source,
    const ModelSafeRoutingInfo& routing_info,
    const std::vector<ModelSafeWorker*>& workers)
    : context_(context),
      source_(source),
      write_transaction_(NULL),
      delegate_(delegate),
      workers_(workers),
      routing_info_(routing_info),
      enabled_groups_(ComputeEnabledGroups(routing_info_, workers_)) {
  status_controller_.reset(new StatusController(routing_info_));
  std::sort(workers_.begin(), workers_.end());
}

SyncSession::~SyncSession() {}

void SyncSession::Coalesce(const SyncSession& session) {
  if (context_ != session.context() || delegate_ != session.delegate_) {
    NOTREACHED();
    return;
  }

  // When we coalesce sessions, the sync update source gets overwritten with the
  // most recent, while the type/payload map gets merged.
  CoalescePayloads(&source_.types, session.source_.types);
  source_.updates_source = session.source_.updates_source;

  std::vector<ModelSafeWorker*> temp;
  std::set_union(workers_.begin(), workers_.end(),
                 session.workers_.begin(), session.workers_.end(),
                 std::back_inserter(temp));
  workers_.swap(temp);

  // We have to update the model safe routing info to the union. In case the
  // same key is present in both pick the one from session.
  for (ModelSafeRoutingInfo::const_iterator it =
       session.routing_info_.begin();
       it != session.routing_info_.end();
       ++it) {
    routing_info_[it->first] = it->second;
  }

  // Now update enabled groups.
  enabled_groups_ = ComputeEnabledGroups(routing_info_, workers_);
}

void SyncSession::RebaseRoutingInfoWithLatest(const SyncSession& session) {
  ModelSafeRoutingInfo temp_routing_info;

  // Take the intersecion and also set the routing info(it->second) from the
  // passed in session.
  for (ModelSafeRoutingInfo::const_iterator it =
       session.routing_info_.begin(); it != session.routing_info_.end();
       ++it) {
    if (routing_info_.find(it->first) != routing_info_.end()) {
      temp_routing_info[it->first] = it->second;
    }
  }

  // Now swap it.
  routing_info_.swap(temp_routing_info);

  // Now update the payload map.
  PurgeStalePayload(&source_.types, session.routing_info_);

  // Now update the workers.
  std::vector<ModelSafeWorker*> temp;
  std::set_intersection(workers_.begin(), workers_.end(),
                 session.workers_.begin(), session.workers_.end(),
                 std::back_inserter(temp));
  workers_.swap(temp);

  // Now update enabled groups.
  enabled_groups_ = ComputeEnabledGroups(routing_info_, workers_);
}

void SyncSession::PrepareForAnotherSyncCycle() {
  source_.updates_source =
      sync_pb::GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION;
  status_controller_.reset(new StatusController(routing_info_));
}

SyncSessionSnapshot SyncSession::TakeSnapshot() const {
  syncable::Directory* dir = context_->directory();

  bool is_share_useable = true;
  syncable::ModelTypeSet initial_sync_ended;
  std::string download_progress_markers[syncable::MODEL_TYPE_COUNT];
  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
       i < syncable::MODEL_TYPE_COUNT; ++i) {
    syncable::ModelType type(syncable::ModelTypeFromInt(i));
    if (routing_info_.count(type) != 0) {
      if (dir->initial_sync_ended_for_type(type))
        initial_sync_ended.Put(type);
      else
        is_share_useable = false;
    }
    dir->GetDownloadProgressAsString(type, &download_progress_markers[i]);
  }

  return SyncSessionSnapshot(
      status_controller_->syncer_status(),
      status_controller_->error(),
      status_controller_->num_server_changes_remaining(),
      is_share_useable,
      initial_sync_ended,
      download_progress_markers,
      HasMoreToSync(),
      delegate_->IsSyncingCurrentlySilenced(),
      status_controller_->unsynced_handles().size(),
      status_controller_->TotalNumEncryptionConflictingItems(),
      status_controller_->TotalNumHierarchyConflictingItems(),
      status_controller_->TotalNumSimpleConflictingItems(),
      status_controller_->TotalNumServerConflictingItems(),
      status_controller_->did_commit_items(),
      source_,
      context_->notifications_enabled(),
      dir->GetEntriesCount(),
      status_controller_->sync_start_time(),
      !Succeeded());
}

void SyncSession::SendEventNotification(SyncEngineEvent::EventCause cause) {
  SyncEngineEvent event(cause);
  const SyncSessionSnapshot& snapshot = TakeSnapshot();
  event.snapshot = &snapshot;

  DVLOG(1) << "Sending event with snapshot: " << snapshot.ToString();
  context()->NotifyListeners(event);
}

bool SyncSession::HasMoreToSync() const {
  const StatusController* status = status_controller_.get();
  return ((status->commit_ids().size() < status->unsynced_handles().size()) &&
      status->syncer_status().num_successful_commits > 0) ||
      status->conflicts_resolved();
      // Or, we have conflicting updates, but we're making progress on
      // resolving them...
}

const std::set<ModelSafeGroup>& SyncSession::GetEnabledGroups() const {
  return enabled_groups_;
}

std::set<ModelSafeGroup> SyncSession::GetEnabledGroupsWithConflicts() const {
  const std::set<ModelSafeGroup>& enabled_groups = GetEnabledGroups();
  std::set<ModelSafeGroup> enabled_groups_with_conflicts;
  for (std::set<ModelSafeGroup>::const_iterator it =
           enabled_groups.begin(); it != enabled_groups.end(); ++it) {
    const sessions::ConflictProgress* conflict_progress =
        status_controller_->GetUnrestrictedConflictProgress(*it);
    if (conflict_progress &&
        (conflict_progress->SimpleConflictingItemsBegin() !=
         conflict_progress->SimpleConflictingItemsEnd())) {
      enabled_groups_with_conflicts.insert(*it);
    }
  }
  return enabled_groups_with_conflicts;
}

std::set<ModelSafeGroup>
    SyncSession::GetEnabledGroupsWithVerifiedUpdates() const {
  const std::set<ModelSafeGroup>& enabled_groups = GetEnabledGroups();
  std::set<ModelSafeGroup> enabled_groups_with_verified_updates;
  for (std::set<ModelSafeGroup>::const_iterator it =
           enabled_groups.begin(); it != enabled_groups.end(); ++it) {
    const UpdateProgress* update_progress =
        status_controller_->GetUnrestrictedUpdateProgress(*it);
    if (update_progress &&
        (update_progress->VerifiedUpdatesBegin() !=
         update_progress->VerifiedUpdatesEnd())) {
      enabled_groups_with_verified_updates.insert(*it);
    }
  }

  return enabled_groups_with_verified_updates;
}

namespace {
// Return true if the command in question was attempted and did not complete
// successfully.
//
bool IsError(SyncerError error) {
  return error != UNSET
      && error != SYNCER_OK
      && error != SERVER_RETURN_MIGRATION_DONE;
}
}  // namespace

bool SyncSession::Succeeded() const {
  const bool download_updates_error =
      IsError(status_controller_->error().last_download_updates_result);
  const bool post_commit_error =
      IsError(status_controller_->error().last_post_commit_result);
  const bool process_commit_response_error =
      IsError(status_controller_->error().last_process_commit_response_result);
  return !download_updates_error
      && !post_commit_error
      && !process_commit_response_error;
}

}  // namespace sessions
}  // namespace browser_sync
<commit_msg>Add #include <iterator> to files that use std::back_inserter. See http://code.google.com/p/chromium/source/search?q=back_inserter+-%23include%5C+%5C%3Citerator%5C%3E&origq=back_inserter+-%23include%5C+%5C%3Citerator%5C%3E&btnG=Search+Trunk<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sync/sessions/sync_session.h"

#include <algorithm>
#include <iterator>

#include "base/logging.h"
#include "sync/syncable/model_type.h"
#include "sync/syncable/syncable.h"

namespace browser_sync {
namespace sessions {

namespace {

std::set<ModelSafeGroup> ComputeEnabledGroups(
    const ModelSafeRoutingInfo& routing_info,
    const std::vector<ModelSafeWorker*>& workers) {
  std::set<ModelSafeGroup> enabled_groups;
  // Project the list of enabled types (i.e., types in the routing
  // info) to a list of enabled groups.
  for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin();
       it != routing_info.end(); ++it) {
    enabled_groups.insert(it->second);
  }
  // GROUP_PASSIVE is always enabled, since that's the group that
  // top-level folders map to.
  enabled_groups.insert(GROUP_PASSIVE);
  if (DCHECK_IS_ON()) {
    // We sometimes create dummy SyncSession objects (see
    // SyncScheduler::InitialSnapshot) so don't check in that case.
    if (!routing_info.empty() || !workers.empty()) {
      std::set<ModelSafeGroup> groups_with_workers;
      for (std::vector<ModelSafeWorker*>::const_iterator it = workers.begin();
           it != workers.end(); ++it) {
        groups_with_workers.insert((*it)->GetModelSafeGroup());
      }
      // All enabled groups should have a corresponding worker.
      DCHECK(std::includes(
          groups_with_workers.begin(), groups_with_workers.end(),
          enabled_groups.begin(), enabled_groups.end()));
    }
  }
  return enabled_groups;
}

}  // namesepace

SyncSession::SyncSession(SyncSessionContext* context, Delegate* delegate,
    const SyncSourceInfo& source,
    const ModelSafeRoutingInfo& routing_info,
    const std::vector<ModelSafeWorker*>& workers)
    : context_(context),
      source_(source),
      write_transaction_(NULL),
      delegate_(delegate),
      workers_(workers),
      routing_info_(routing_info),
      enabled_groups_(ComputeEnabledGroups(routing_info_, workers_)) {
  status_controller_.reset(new StatusController(routing_info_));
  std::sort(workers_.begin(), workers_.end());
}

SyncSession::~SyncSession() {}

void SyncSession::Coalesce(const SyncSession& session) {
  if (context_ != session.context() || delegate_ != session.delegate_) {
    NOTREACHED();
    return;
  }

  // When we coalesce sessions, the sync update source gets overwritten with the
  // most recent, while the type/payload map gets merged.
  CoalescePayloads(&source_.types, session.source_.types);
  source_.updates_source = session.source_.updates_source;

  std::vector<ModelSafeWorker*> temp;
  std::set_union(workers_.begin(), workers_.end(),
                 session.workers_.begin(), session.workers_.end(),
                 std::back_inserter(temp));
  workers_.swap(temp);

  // We have to update the model safe routing info to the union. In case the
  // same key is present in both pick the one from session.
  for (ModelSafeRoutingInfo::const_iterator it =
       session.routing_info_.begin();
       it != session.routing_info_.end();
       ++it) {
    routing_info_[it->first] = it->second;
  }

  // Now update enabled groups.
  enabled_groups_ = ComputeEnabledGroups(routing_info_, workers_);
}

void SyncSession::RebaseRoutingInfoWithLatest(const SyncSession& session) {
  ModelSafeRoutingInfo temp_routing_info;

  // Take the intersecion and also set the routing info(it->second) from the
  // passed in session.
  for (ModelSafeRoutingInfo::const_iterator it =
       session.routing_info_.begin(); it != session.routing_info_.end();
       ++it) {
    if (routing_info_.find(it->first) != routing_info_.end()) {
      temp_routing_info[it->first] = it->second;
    }
  }

  // Now swap it.
  routing_info_.swap(temp_routing_info);

  // Now update the payload map.
  PurgeStalePayload(&source_.types, session.routing_info_);

  // Now update the workers.
  std::vector<ModelSafeWorker*> temp;
  std::set_intersection(workers_.begin(), workers_.end(),
                 session.workers_.begin(), session.workers_.end(),
                 std::back_inserter(temp));
  workers_.swap(temp);

  // Now update enabled groups.
  enabled_groups_ = ComputeEnabledGroups(routing_info_, workers_);
}

void SyncSession::PrepareForAnotherSyncCycle() {
  source_.updates_source =
      sync_pb::GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION;
  status_controller_.reset(new StatusController(routing_info_));
}

SyncSessionSnapshot SyncSession::TakeSnapshot() const {
  syncable::Directory* dir = context_->directory();

  bool is_share_useable = true;
  syncable::ModelTypeSet initial_sync_ended;
  std::string download_progress_markers[syncable::MODEL_TYPE_COUNT];
  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
       i < syncable::MODEL_TYPE_COUNT; ++i) {
    syncable::ModelType type(syncable::ModelTypeFromInt(i));
    if (routing_info_.count(type) != 0) {
      if (dir->initial_sync_ended_for_type(type))
        initial_sync_ended.Put(type);
      else
        is_share_useable = false;
    }
    dir->GetDownloadProgressAsString(type, &download_progress_markers[i]);
  }

  return SyncSessionSnapshot(
      status_controller_->syncer_status(),
      status_controller_->error(),
      status_controller_->num_server_changes_remaining(),
      is_share_useable,
      initial_sync_ended,
      download_progress_markers,
      HasMoreToSync(),
      delegate_->IsSyncingCurrentlySilenced(),
      status_controller_->unsynced_handles().size(),
      status_controller_->TotalNumEncryptionConflictingItems(),
      status_controller_->TotalNumHierarchyConflictingItems(),
      status_controller_->TotalNumSimpleConflictingItems(),
      status_controller_->TotalNumServerConflictingItems(),
      status_controller_->did_commit_items(),
      source_,
      context_->notifications_enabled(),
      dir->GetEntriesCount(),
      status_controller_->sync_start_time(),
      !Succeeded());
}

void SyncSession::SendEventNotification(SyncEngineEvent::EventCause cause) {
  SyncEngineEvent event(cause);
  const SyncSessionSnapshot& snapshot = TakeSnapshot();
  event.snapshot = &snapshot;

  DVLOG(1) << "Sending event with snapshot: " << snapshot.ToString();
  context()->NotifyListeners(event);
}

bool SyncSession::HasMoreToSync() const {
  const StatusController* status = status_controller_.get();
  return ((status->commit_ids().size() < status->unsynced_handles().size()) &&
      status->syncer_status().num_successful_commits > 0) ||
      status->conflicts_resolved();
      // Or, we have conflicting updates, but we're making progress on
      // resolving them...
}

const std::set<ModelSafeGroup>& SyncSession::GetEnabledGroups() const {
  return enabled_groups_;
}

std::set<ModelSafeGroup> SyncSession::GetEnabledGroupsWithConflicts() const {
  const std::set<ModelSafeGroup>& enabled_groups = GetEnabledGroups();
  std::set<ModelSafeGroup> enabled_groups_with_conflicts;
  for (std::set<ModelSafeGroup>::const_iterator it =
           enabled_groups.begin(); it != enabled_groups.end(); ++it) {
    const sessions::ConflictProgress* conflict_progress =
        status_controller_->GetUnrestrictedConflictProgress(*it);
    if (conflict_progress &&
        (conflict_progress->SimpleConflictingItemsBegin() !=
         conflict_progress->SimpleConflictingItemsEnd())) {
      enabled_groups_with_conflicts.insert(*it);
    }
  }
  return enabled_groups_with_conflicts;
}

std::set<ModelSafeGroup>
    SyncSession::GetEnabledGroupsWithVerifiedUpdates() const {
  const std::set<ModelSafeGroup>& enabled_groups = GetEnabledGroups();
  std::set<ModelSafeGroup> enabled_groups_with_verified_updates;
  for (std::set<ModelSafeGroup>::const_iterator it =
           enabled_groups.begin(); it != enabled_groups.end(); ++it) {
    const UpdateProgress* update_progress =
        status_controller_->GetUnrestrictedUpdateProgress(*it);
    if (update_progress &&
        (update_progress->VerifiedUpdatesBegin() !=
         update_progress->VerifiedUpdatesEnd())) {
      enabled_groups_with_verified_updates.insert(*it);
    }
  }

  return enabled_groups_with_verified_updates;
}

namespace {
// Return true if the command in question was attempted and did not complete
// successfully.
//
bool IsError(SyncerError error) {
  return error != UNSET
      && error != SYNCER_OK
      && error != SERVER_RETURN_MIGRATION_DONE;
}
}  // namespace

bool SyncSession::Succeeded() const {
  const bool download_updates_error =
      IsError(status_controller_->error().last_download_updates_result);
  const bool post_commit_error =
      IsError(status_controller_->error().last_post_commit_result);
  const bool process_commit_response_error =
      IsError(status_controller_->error().last_process_commit_response_result);
  return !download_updates_error
      && !post_commit_error
      && !process_commit_response_error;
}

}  // namespace sessions
}  // namespace browser_sync
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Mohammad Mehdi Saboorian
 *
 * This is part of moor, a wrapper for libarchive
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <moor/archive_reader.hpp>
#include <moor/archive_writer.hpp>

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <system_error>
#include <vector>

using namespace moor;

static bool testDoesNotExist()
{
  try
  {
    ArchiveReader badReader("xxxx_does_not_exist");
    return true; // Failure, we expect an exception
  }
  catch (const std::system_error& ex)
  {
    std::cerr << "Expected exception reading non-existent file: " << ex.what() << '\n';
    if (ex.code() != std::errc::no_such_file_or_directory)
    {
      std::cerr << "Unexpected error code for non-existent file\n";
      return true;
    }

    return false;
  }
}

static void writeArrayToFile(const std::string& path,
                             const std::vector<unsigned char>& v)
{
  std::ofstream of(path, std::ios::binary);
  for (auto a : v)
  {
    of << a;
  }
}

static bool testArchiveWriteImpl(ArchiveWriter& compressor)
{
  compressor.addFile("test_data_dir/bar.txt");
  compressor.addDirectory("test_data_dir/foo_dir");
  char a[] = { 64, 65, 66, 67, 68 };
  std::vector<char> l(10, 'A');
  std::vector<char> v(10, 'B');

  compressor.addFile("a_array.txt", a, sizeof(a));
  compressor.addFile("b_list.txt", l.begin(), l.end());
  compressor.addFile("vector_a.txt", v.begin(), v.end());
  compressor.close();

  return false;
}

// Write result directly to path
static bool testArchiveWriteFile(const std::string& path)
{
  try
  {
    ArchiveWriter compressor(path, Format::PAX, Filter::Gzip);
    return testArchiveWriteImpl(compressor);
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error writing file archive: " << ex.what() << '\n';
    return true;
  }
}

// Write to path and first go through temporary buffer
static bool testArchiveWriteMemory(const std::string& path)
{
  try
  {
    std::vector<unsigned char> lout;
    ArchiveWriter compressor(lout, Format::PAX, Filter::Gzip);
    if (testArchiveWriteImpl(compressor))
    {
      return true;
    }

    writeArrayToFile(path, lout);
    return false;
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error writing memory archive: " << ex.what() << '\n';
    return true;
  }
}

static bool testArchiveRead(const std::string& path)
{
  try
  {
    ArchiveReader reader1(path);
    std::ifstream iff(path, std::ios::binary);
    iff.seekg(0, std::ios::end);
    auto size = iff.tellg();
    iff.seekg(0, std::ios::beg);
    std::vector<unsigned char> ff(size);

    while (iff.good())
    {
      iff.read(reinterpret_cast<char*>(ff.data()), size);
    }

    ArchiveReader reader(std::move(ff));
    auto data = reader.extractNext();
    while (data.first.length() > 0)
    {
      std::cout << data.first << " : " << data.second.size() << '\n';
      data = reader.extractNext();
    }

    data = reader1.extractNext();
    while (data.first.length() > 0)
    {
      std::cout << data.first << " : " << data.second.size() << '\n';
      data = reader1.extractNext();
    }

    return false;
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error reading archive: " << ex.what() << '\n';
    return true;
  }
}

int main()
{
  if (testDoesNotExist())
  {
    return 1;
  }

  if (testArchiveWriteFile("test_write_file.tar.gz"))
  {
    return 1;
  }

  if (testArchiveRead("test_write_file.tar.gz"))
  {
    return 1;
  }

  if (testArchiveWriteMemory("test_write_memory.tar.gz"))
  {
    return 1;
  }

  if (testArchiveRead("test_write_memory.tar.gz"))
  {
    return 1;
  }

  return 0;
}
<commit_msg>Use .empty() instead of compare .length() to 0<commit_after>/*
 * Copyright (c) 2013 Mohammad Mehdi Saboorian
 *
 * This is part of moor, a wrapper for libarchive
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <moor/archive_reader.hpp>
#include <moor/archive_writer.hpp>

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <system_error>
#include <vector>

using namespace moor;

static bool testDoesNotExist()
{
  try
  {
    ArchiveReader badReader("xxxx_does_not_exist");
    return true; // Failure, we expect an exception
  }
  catch (const std::system_error& ex)
  {
    std::cerr << "Expected exception reading non-existent file: " << ex.what() << '\n';
    if (ex.code() != std::errc::no_such_file_or_directory)
    {
      std::cerr << "Unexpected error code for non-existent file\n";
      return true;
    }

    return false;
  }
}

static void writeArrayToFile(const std::string& path,
                             const std::vector<unsigned char>& v)
{
  std::ofstream of(path, std::ios::binary);
  for (auto a : v)
  {
    of << a;
  }
}

static bool testArchiveWriteImpl(ArchiveWriter& compressor)
{
  compressor.addFile("test_data_dir/bar.txt");
  compressor.addDirectory("test_data_dir/foo_dir");
  char a[] = { 64, 65, 66, 67, 68 };
  std::vector<char> l(10, 'A');
  std::vector<char> v(10, 'B');

  compressor.addFile("a_array.txt", a, sizeof(a));
  compressor.addFile("b_list.txt", l.begin(), l.end());
  compressor.addFile("vector_a.txt", v.begin(), v.end());
  compressor.close();

  return false;
}

// Write result directly to path
static bool testArchiveWriteFile(const std::string& path)
{
  try
  {
    ArchiveWriter compressor(path, Format::PAX, Filter::Gzip);
    return testArchiveWriteImpl(compressor);
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error writing file archive: " << ex.what() << '\n';
    return true;
  }
}

// Write to path and first go through temporary buffer
static bool testArchiveWriteMemory(const std::string& path)
{
  try
  {
    std::vector<unsigned char> lout;
    ArchiveWriter compressor(lout, Format::PAX, Filter::Gzip);
    if (testArchiveWriteImpl(compressor))
    {
      return true;
    }

    writeArrayToFile(path, lout);
    return false;
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error writing memory archive: " << ex.what() << '\n';
    return true;
  }
}

static bool testArchiveRead(const std::string& path)
{
  try
  {
    ArchiveReader reader1(path);
    std::ifstream iff(path, std::ios::binary);
    iff.seekg(0, std::ios::end);
    auto size = iff.tellg();
    iff.seekg(0, std::ios::beg);
    std::vector<unsigned char> ff(size);

    while (iff.good())
    {
      iff.read(reinterpret_cast<char*>(ff.data()), size);
    }

    ArchiveReader reader(std::move(ff));
    auto data = reader.extractNext();
    while (!data.first.empty())
    {
      std::cout << data.first << " : " << data.second.size() << '\n';
      data = reader.extractNext();
    }

    data = reader1.extractNext();
    while (!data.first.empty())
    {
      std::cout << data.first << " : " << data.second.size() << '\n';
      data = reader1.extractNext();
    }

    return false;
  }
  catch (const std::runtime_error& ex)
  {
    std::cerr << "Error reading archive: " << ex.what() << '\n';
    return true;
  }
}

int main()
{
  if (testDoesNotExist())
  {
    return 1;
  }

  if (testArchiveWriteFile("test_write_file.tar.gz"))
  {
    return 1;
  }

  if (testArchiveRead("test_write_file.tar.gz"))
  {
    return 1;
  }

  if (testArchiveWriteMemory("test_write_memory.tar.gz"))
  {
    return 1;
  }

  if (testArchiveRead("test_write_memory.tar.gz"))
  {
    return 1;
  }

  return 0;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// stl
#include <algorithm>
#include <future>
#include <atomic>

#include <mapnik/load_map.hpp>

#include "runner.hpp"

namespace visual_tests
{

class renderer_visitor
{
public:
    renderer_visitor(std::string const & name,
                     mapnik::Map & map,
                     map_size const & tiles,
                     double scale_factor,
                     result_list & results,
                     report_type & report,
                     std::size_t iterations,
                     bool is_fail_limit,
                     std::atomic<std::size_t> & fail_count)
        : name_(name),
          map_(map),
          tiles_(tiles),
          scale_factor_(scale_factor),
          results_(results),
          report_(report),
          iterations_(iterations),
          is_fail_limit_(is_fail_limit),
          fail_count_(fail_count)
    {
    }

    template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
    void operator()(T const & renderer)
    {
        test(renderer);
    }

    template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
    void operator()(T const & renderer)
    {
        if (tiles_.width == 1 && tiles_.height == 1)
        {
            test(renderer);
        }
    }

private:
    template <typename T>
    void test(T const & renderer)
    {
        map_size size { map_.width(), map_.height() };
        std::chrono::high_resolution_clock::time_point start(std::chrono::high_resolution_clock::now());
        for (std::size_t i = iterations_ ; i > 0; i--)
        {
            typename T::image_type image(render(renderer));
            if (i == 1)
            {
                std::chrono::high_resolution_clock::time_point end(std::chrono::high_resolution_clock::now());
                result r(renderer.report(image, name_, size, tiles_, scale_factor_));
                r.duration = end - start;
                mapnik::util::apply_visitor(report_visitor(r), report_);
                results_.push_back(std::move(r));
                if (is_fail_limit_ && r.state == STATE_FAIL)
                {
                    ++fail_count_;
                }
            }
        }
    }

    template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
    typename T::image_type render(T const & renderer)
    {
        if (tiles_.width == 1 && tiles_.height == 1)
        {
            return renderer.render(map_, scale_factor_);
        }
        else
        {
            return renderer.render(map_, scale_factor_, tiles_);
        }
    }

    template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
    typename T::image_type render(T const & renderer)
    {
        return renderer.render(map_, scale_factor_);
    }

    std::string const & name_;
    mapnik::Map & map_;
    map_size const & tiles_;
    double scale_factor_;
    result_list & results_;
    report_type & report_;
    std::size_t iterations_;
    bool is_fail_limit_;
    std::atomic<std::size_t> & fail_count_;
};

runner::runner(runner::path_type const & styles_dir,
               runner::path_type const & output_dir,
               runner::path_type const & reference_dir,
               bool overwrite,
               std::size_t iterations,
               std::size_t fail_limit,
               std::size_t jobs)
    : styles_dir_(styles_dir),
      output_dir_(output_dir),
      reference_dir_(reference_dir),
      jobs_(jobs),
      iterations_(iterations),
      fail_limit_(fail_limit),
      renderers_{ renderer<agg_renderer>(output_dir_, reference_dir_, overwrite)
#if defined(HAVE_CAIRO)
                  ,renderer<cairo_renderer>(output_dir_, reference_dir_, overwrite)
#endif
#if defined(SVG_RENDERER)
                  ,renderer<svg_renderer>(output_dir_, reference_dir_, overwrite)
#endif
#if defined(GRID_RENDERER)
                  ,renderer<grid_renderer>(output_dir_, reference_dir_, overwrite)
#endif
                }
{
}

result_list runner::test_all(report_type & report) const
{
    boost::filesystem::directory_iterator begin(styles_dir_);
    boost::filesystem::directory_iterator end;
    std::vector<runner::path_type> files(begin, end);
    return test_parallel(files, report, jobs_);
}

result_list runner::test(std::vector<std::string> const & style_names, report_type & report) const
{
    std::vector<runner::path_type> files(style_names.size());
    std::transform(style_names.begin(), style_names.end(), std::back_inserter(files),
        [&](runner::path_type const & name)
        {
            return (name.extension() == ".xml") ? name : (styles_dir_ / (name.string() + ".xml"));
        });
    return test_parallel(files, report, jobs_);
}

result_list runner::test_parallel(std::vector<runner::path_type> const & files, report_type & report, std::size_t jobs) const
{
    result_list results;

    if (files.empty())
    {
        return results;
    }

    if (jobs == 0)
    {
        jobs = 1;
    }

    std::size_t chunk_size = files.size() / jobs;

    if (chunk_size == 0)
    {
        chunk_size = files.size();
        jobs = 1;
    }

    std::launch launch(jobs == 1 ? std::launch::deferred : std::launch::async);
    std::vector<std::future<result_list>> futures(jobs);
    std::atomic<std::size_t> fail_count(0);

    for (std::size_t i = 0; i < jobs; i++)
    {
        files_iterator begin(files.begin() + i * chunk_size);
        files_iterator end(files.begin() + (i + 1) * chunk_size);

        // Handle remainder of files.size() / jobs
        if (i == jobs - 1)
        {
            end = files.end();
        }

        futures[i] = std::async(launch, &runner::test_range, this, begin, end, std::ref(report), std::ref(fail_count));
    }

    for (auto & f : futures)
    {
        result_list r = f.get();
        std::move(r.begin(), r.end(), std::back_inserter(results));
    }

    return results;
}

result_list runner::test_range(files_iterator begin,
                               files_iterator end,
                               std::reference_wrapper<report_type> report,
                               std::reference_wrapper<std::atomic<std::size_t>> fail_count) const
{
    config defaults;
    result_list results;

    for (runner::files_iterator i = begin; i != end; i++)
    {
        runner::path_type const & file = *i;
        if (file.extension() == ".xml")
        {
            try
            {
                result_list r = test_one(file, defaults, report, fail_count.get());
                std::move(r.begin(), r.end(), std::back_inserter(results));
            }
            catch (std::exception const& ex)
            {
                result r;
                r.state = STATE_ERROR;
                r.name = file.string();
                r.error_message = ex.what();
                results.emplace_back(r);
                mapnik::util::apply_visitor(report_visitor(r), report.get());
                ++fail_count.get();
            }
        }
        if (fail_limit_ && fail_count.get() >= fail_limit_)
        {
            break;
        }
    }

    return results;
}

result_list runner::test_one(runner::path_type const& style_path,
                             config cfg,
                             report_type & report,
                             std::atomic<std::size_t> & fail_count) const
{
    mapnik::Map map(cfg.sizes.front().width, cfg.sizes.front().height);
    result_list results;

    try
    {
        mapnik::load_map(map, style_path.string(), true);
    }
    catch (std::exception const& ex)
    {
        std::string what = ex.what();
        if (what.find("Could not create datasource") != std::string::npos ||
            what.find("Postgis Plugin: could not connect to server") != std::string::npos)
        {
            return results;
        }
        throw;
    }

    mapnik::parameters const & params = map.get_extra_parameters();

    boost::optional<mapnik::value_integer> status = params.get<mapnik::value_integer>("status", cfg.status);

    if (!*status)
    {
        return results;
    }

    boost::optional<std::string> sizes = params.get<std::string>("sizes");

    if (sizes)
    {
        cfg.sizes.clear();
        parse_map_sizes(*sizes, cfg.sizes);
    }

    boost::optional<std::string> tiles = params.get<std::string>("tiles");

    if (tiles)
    {
        cfg.tiles.clear();
        parse_map_sizes(*tiles, cfg.tiles);
    }

    boost::optional<std::string> bbox_string = params.get<std::string>("bbox");
    mapnik::box2d<double> box;

    if (bbox_string)
    {
        box.from_string(*bbox_string);
    }

    std::string name(style_path.stem().string());

    for (auto const & size : cfg.sizes)
    {
        for (auto const & scale_factor : cfg.scales)
        {
            for (auto const & tiles_count : cfg.tiles)
            {
                if (!tiles_count.width || !tiles_count.height)
                {
                    throw std::runtime_error("Cannot render zero tiles.");
                }
                if (size.width % tiles_count.width || size.height % tiles_count.height)
                {
                    throw std::runtime_error("Tile size is not an integer.");
                }

                for (auto const & ren : renderers_)
                {
                    map.resize(size.width, size.height);
                    if (box.valid())
                    {
                        map.zoom_to_box(box);
                    }
                    else
                    {
                        map.zoom_all();
                    }
                    mapnik::util::apply_visitor(renderer_visitor(name,
                                                                 map,
                                                                 tiles_count,
                                                                 scale_factor,
                                                                 results,
                                                                 report,
                                                                 iterations_,
                                                                 fail_limit_,
                                                                 fail_count), ren);
                    if (fail_limit_ && fail_count >= fail_limit_)
                    {
                        return results;
                    }
                }
            }
        }
    }

    return results;
}

void runner::parse_map_sizes(std::string const & str, std::vector<map_size> & sizes) const
{
    boost::spirit::ascii::space_type space;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    if (!boost::spirit::qi::phrase_parse(iter, end, map_sizes_parser_, space, sizes))
    {
        throw std::runtime_error("Failed to parse list of sizes: '" + str + "'");
    }
}

}

<commit_msg>visual tests: set test duration to zero on error<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// stl
#include <algorithm>
#include <future>
#include <atomic>

#include <mapnik/load_map.hpp>

#include "runner.hpp"

namespace visual_tests
{

class renderer_visitor
{
public:
    renderer_visitor(std::string const & name,
                     mapnik::Map & map,
                     map_size const & tiles,
                     double scale_factor,
                     result_list & results,
                     report_type & report,
                     std::size_t iterations,
                     bool is_fail_limit,
                     std::atomic<std::size_t> & fail_count)
        : name_(name),
          map_(map),
          tiles_(tiles),
          scale_factor_(scale_factor),
          results_(results),
          report_(report),
          iterations_(iterations),
          is_fail_limit_(is_fail_limit),
          fail_count_(fail_count)
    {
    }

    template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
    void operator()(T const & renderer)
    {
        test(renderer);
    }

    template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
    void operator()(T const & renderer)
    {
        if (tiles_.width == 1 && tiles_.height == 1)
        {
            test(renderer);
        }
    }

private:
    template <typename T>
    void test(T const & renderer)
    {
        map_size size { map_.width(), map_.height() };
        std::chrono::high_resolution_clock::time_point start(std::chrono::high_resolution_clock::now());
        for (std::size_t i = iterations_ ; i > 0; i--)
        {
            typename T::image_type image(render(renderer));
            if (i == 1)
            {
                std::chrono::high_resolution_clock::time_point end(std::chrono::high_resolution_clock::now());
                result r(renderer.report(image, name_, size, tiles_, scale_factor_));
                r.duration = end - start;
                mapnik::util::apply_visitor(report_visitor(r), report_);
                results_.push_back(std::move(r));
                if (is_fail_limit_ && r.state == STATE_FAIL)
                {
                    ++fail_count_;
                }
            }
        }
    }

    template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
    typename T::image_type render(T const & renderer)
    {
        if (tiles_.width == 1 && tiles_.height == 1)
        {
            return renderer.render(map_, scale_factor_);
        }
        else
        {
            return renderer.render(map_, scale_factor_, tiles_);
        }
    }

    template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
    typename T::image_type render(T const & renderer)
    {
        return renderer.render(map_, scale_factor_);
    }

    std::string const & name_;
    mapnik::Map & map_;
    map_size const & tiles_;
    double scale_factor_;
    result_list & results_;
    report_type & report_;
    std::size_t iterations_;
    bool is_fail_limit_;
    std::atomic<std::size_t> & fail_count_;
};

runner::runner(runner::path_type const & styles_dir,
               runner::path_type const & output_dir,
               runner::path_type const & reference_dir,
               bool overwrite,
               std::size_t iterations,
               std::size_t fail_limit,
               std::size_t jobs)
    : styles_dir_(styles_dir),
      output_dir_(output_dir),
      reference_dir_(reference_dir),
      jobs_(jobs),
      iterations_(iterations),
      fail_limit_(fail_limit),
      renderers_{ renderer<agg_renderer>(output_dir_, reference_dir_, overwrite)
#if defined(HAVE_CAIRO)
                  ,renderer<cairo_renderer>(output_dir_, reference_dir_, overwrite)
#endif
#if defined(SVG_RENDERER)
                  ,renderer<svg_renderer>(output_dir_, reference_dir_, overwrite)
#endif
#if defined(GRID_RENDERER)
                  ,renderer<grid_renderer>(output_dir_, reference_dir_, overwrite)
#endif
                }
{
}

result_list runner::test_all(report_type & report) const
{
    boost::filesystem::directory_iterator begin(styles_dir_);
    boost::filesystem::directory_iterator end;
    std::vector<runner::path_type> files(begin, end);
    return test_parallel(files, report, jobs_);
}

result_list runner::test(std::vector<std::string> const & style_names, report_type & report) const
{
    std::vector<runner::path_type> files(style_names.size());
    std::transform(style_names.begin(), style_names.end(), std::back_inserter(files),
        [&](runner::path_type const & name)
        {
            return (name.extension() == ".xml") ? name : (styles_dir_ / (name.string() + ".xml"));
        });
    return test_parallel(files, report, jobs_);
}

result_list runner::test_parallel(std::vector<runner::path_type> const & files, report_type & report, std::size_t jobs) const
{
    result_list results;

    if (files.empty())
    {
        return results;
    }

    if (jobs == 0)
    {
        jobs = 1;
    }

    std::size_t chunk_size = files.size() / jobs;

    if (chunk_size == 0)
    {
        chunk_size = files.size();
        jobs = 1;
    }

    std::launch launch(jobs == 1 ? std::launch::deferred : std::launch::async);
    std::vector<std::future<result_list>> futures(jobs);
    std::atomic<std::size_t> fail_count(0);

    for (std::size_t i = 0; i < jobs; i++)
    {
        files_iterator begin(files.begin() + i * chunk_size);
        files_iterator end(files.begin() + (i + 1) * chunk_size);

        // Handle remainder of files.size() / jobs
        if (i == jobs - 1)
        {
            end = files.end();
        }

        futures[i] = std::async(launch, &runner::test_range, this, begin, end, std::ref(report), std::ref(fail_count));
    }

    for (auto & f : futures)
    {
        result_list r = f.get();
        std::move(r.begin(), r.end(), std::back_inserter(results));
    }

    return results;
}

result_list runner::test_range(files_iterator begin,
                               files_iterator end,
                               std::reference_wrapper<report_type> report,
                               std::reference_wrapper<std::atomic<std::size_t>> fail_count) const
{
    config defaults;
    result_list results;

    for (runner::files_iterator i = begin; i != end; i++)
    {
        runner::path_type const & file = *i;
        if (file.extension() == ".xml")
        {
            try
            {
                result_list r = test_one(file, defaults, report, fail_count.get());
                std::move(r.begin(), r.end(), std::back_inserter(results));
            }
            catch (std::exception const& ex)
            {
                result r;
                r.state = STATE_ERROR;
                r.name = file.string();
                r.error_message = ex.what();
                r.duration = std::chrono::high_resolution_clock::duration::zero();
                results.emplace_back(r);
                mapnik::util::apply_visitor(report_visitor(r), report.get());
                ++fail_count.get();
            }
        }
        if (fail_limit_ && fail_count.get() >= fail_limit_)
        {
            break;
        }
    }

    return results;
}

result_list runner::test_one(runner::path_type const& style_path,
                             config cfg,
                             report_type & report,
                             std::atomic<std::size_t> & fail_count) const
{
    mapnik::Map map(cfg.sizes.front().width, cfg.sizes.front().height);
    result_list results;

    try
    {
        mapnik::load_map(map, style_path.string(), true);
    }
    catch (std::exception const& ex)
    {
        std::string what = ex.what();
        if (what.find("Could not create datasource") != std::string::npos ||
            what.find("Postgis Plugin: could not connect to server") != std::string::npos)
        {
            return results;
        }
        throw;
    }

    mapnik::parameters const & params = map.get_extra_parameters();

    boost::optional<mapnik::value_integer> status = params.get<mapnik::value_integer>("status", cfg.status);

    if (!*status)
    {
        return results;
    }

    boost::optional<std::string> sizes = params.get<std::string>("sizes");

    if (sizes)
    {
        cfg.sizes.clear();
        parse_map_sizes(*sizes, cfg.sizes);
    }

    boost::optional<std::string> tiles = params.get<std::string>("tiles");

    if (tiles)
    {
        cfg.tiles.clear();
        parse_map_sizes(*tiles, cfg.tiles);
    }

    boost::optional<std::string> bbox_string = params.get<std::string>("bbox");
    mapnik::box2d<double> box;

    if (bbox_string)
    {
        box.from_string(*bbox_string);
    }

    std::string name(style_path.stem().string());

    for (auto const & size : cfg.sizes)
    {
        for (auto const & scale_factor : cfg.scales)
        {
            for (auto const & tiles_count : cfg.tiles)
            {
                if (!tiles_count.width || !tiles_count.height)
                {
                    throw std::runtime_error("Cannot render zero tiles.");
                }
                if (size.width % tiles_count.width || size.height % tiles_count.height)
                {
                    throw std::runtime_error("Tile size is not an integer.");
                }

                for (auto const & ren : renderers_)
                {
                    map.resize(size.width, size.height);
                    if (box.valid())
                    {
                        map.zoom_to_box(box);
                    }
                    else
                    {
                        map.zoom_all();
                    }
                    mapnik::util::apply_visitor(renderer_visitor(name,
                                                                 map,
                                                                 tiles_count,
                                                                 scale_factor,
                                                                 results,
                                                                 report,
                                                                 iterations_,
                                                                 fail_limit_,
                                                                 fail_count), ren);
                    if (fail_limit_ && fail_count >= fail_limit_)
                    {
                        return results;
                    }
                }
            }
        }
    }

    return results;
}

void runner::parse_map_sizes(std::string const & str, std::vector<map_size> & sizes) const
{
    boost::spirit::ascii::space_type space;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    if (!boost::spirit::qi::phrase_parse(iter, end, map_sizes_parser_, space, sizes))
    {
        throw std::runtime_error("Failed to parse list of sizes: '" + str + "'");
    }
}

}

<|endoftext|>