<commit_before>#include "hecl/ClientProcess.hpp"
#include "hecl/Database.hpp"
#include "athena/FileReader.hpp"
#include "hecl/Blender/Connection.hpp"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <sys/wait.h>
#endif

#define HECL_MULTIPROCESSOR 1

namespace hecl
{
static logvisor::Module CP_Log("hecl::ClientProcess");

ThreadLocalPtr<ClientProcess::Worker> ClientProcess::ThreadWorker;

static int GetCPUCount()
{
#if _WIN32
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
#else
    return sysconf(_SC_NPROCESSORS_ONLN);
#endif
}

void ClientProcess::BufferTransaction::run(blender::Token& btok)
{
    athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
    if (r.hasError())
    {
        CP_Log.report(logvisor::Fatal, _S("unable to background-buffer '%s'"),
                      m_path.getAbsolutePath().data());
        return;
    }
    if (m_offset)
        r.seek(m_offset, athena::Begin);
    r.readBytesToBuf(m_targetBuf, m_maxLen);
    m_complete = true;
}

void ClientProcess::CookTransaction::run(blender::Token& btok)
{
    m_dataSpec->setThreadProject();
    m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok);
    m_complete = true;
}

void ClientProcess::LambdaTransaction::run(blender::Token& btok)
{
    m_func(btok);
    m_complete = true;
}

ClientProcess::Worker::Worker(ClientProcess& proc, int idx)
: m_proc(proc), m_idx(idx)
{
    m_thr = std::thread(std::bind(&Worker::proc, this));
}

void ClientProcess::Worker::proc()
{
    ClientProcess::ThreadWorker.reset(this);

    char thrName[64];
    snprintf(thrName, 64, "HECL Client Worker %d", m_idx);
    logvisor::RegisterThreadName(thrName);

    while (m_proc.m_running)
    {
        std::unique_lock<std::mutex> lk(m_proc.m_mutex);
        if (!m_didInit)
        {
            m_proc.m_initCv.notify_one();
            m_didInit = true;
        }
        while (m_proc.m_running && m_proc.m_pendingQueue.size())
        {
            std::shared_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
            ++m_proc.m_inProgress;
            m_proc.m_pendingQueue.pop_front();
            lk.unlock();
            trans->run(m_blendTok);
            lk.lock();
            m_proc.m_completedQueue.push_back(std::move(trans));
            --m_proc.m_inProgress;
        }
        m_proc.m_waitCv.notify_one();
        if (!m_proc.m_running)
            break;
        m_proc.m_cv.wait(lk);
    }
    m_blendTok.shutdown();
}

ClientProcess::ClientProcess(int verbosityLevel, bool fast, bool force)
: m_verbosity(verbosityLevel), m_fast(fast), m_force(force)
{
#ifdef HECL_MULTIPROCESSOR
    const int cpuCount = GetCPUCount();
#else
    constexpr int cpuCount = 1;
#endif
    m_workers.reserve(cpuCount);
    for (int i=0 ; i<cpuCount ; ++i)
    {
        std::unique_lock<std::mutex> lk(m_mutex);
        m_workers.emplace_back(*this, m_workers.size());
        m_initCv.wait(lk);
    }
}

std::shared_ptr<const ClientProcess::BufferTransaction>
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
                                    size_t maxLen, size_t offset)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

std::shared_ptr<const ClientProcess::CookTransaction>
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto ret = std::make_shared<CookTransaction>(*this, path, spec);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

std::shared_ptr<const ClientProcess::LambdaTransaction>
ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func));
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok)
{
    if (spec->canCook(path, btok))
    {
        const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
        if (specEnt)
        {
            hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
            if (m_fast)
                cooked = cooked.getWithExtension(_S(".fast"));
            cooked.makeDirChain(false);
            if (m_force || cooked.getPathType() == ProjectPath::Type::None ||
                path.getModtime() > cooked.getModtime())
            {
                if (path.getAuxInfo().empty())
                    LogModule.report(logvisor::Info, _S("Cooking %s"),
                                     path.getRelativePath().data());
                else
                    LogModule.report(logvisor::Info, _S("Cooking %s|%s"),
                                     path.getRelativePath().data(),
                                     path.getAuxInfo().data());
                spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
            }
            return true;
        }
    }
    return false;
}

void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    queue.swap(m_completedQueue);
}

void ClientProcess::waitUntilComplete()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    while (isBusy())
        m_waitCv.wait(lk);
}

void ClientProcess::shutdown()
{
    if (!m_running)
        return;
    std::unique_lock<std::mutex> lk(m_mutex);
    m_pendingQueue.clear();
    m_running = false;
    m_cv.notify_all();
    lk.unlock();
    for (Worker& worker : m_workers)
        if (worker.m_thr.joinable())
            worker.m_thr.join();
}

}
<commit_msg>Minor macro adjustment<commit_after>#include "hecl/ClientProcess.hpp"
#include "hecl/Database.hpp"
#include "athena/FileReader.hpp"
#include "hecl/Blender/Connection.hpp"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <sys/wait.h>
#endif

#define HECL_MULTIPROCESSOR 1

namespace hecl
{
static logvisor::Module CP_Log("hecl::ClientProcess");

ThreadLocalPtr<ClientProcess::Worker> ClientProcess::ThreadWorker;

static int GetCPUCount()
{
#if _WIN32
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
#else
    return sysconf(_SC_NPROCESSORS_ONLN);
#endif
}

void ClientProcess::BufferTransaction::run(blender::Token& btok)
{
    athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
    if (r.hasError())
    {
        CP_Log.report(logvisor::Fatal, _S("unable to background-buffer '%s'"),
                      m_path.getAbsolutePath().data());
        return;
    }
    if (m_offset)
        r.seek(m_offset, athena::Begin);
    r.readBytesToBuf(m_targetBuf, m_maxLen);
    m_complete = true;
}

void ClientProcess::CookTransaction::run(blender::Token& btok)
{
    m_dataSpec->setThreadProject();
    m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok);
    m_complete = true;
}

void ClientProcess::LambdaTransaction::run(blender::Token& btok)
{
    m_func(btok);
    m_complete = true;
}

ClientProcess::Worker::Worker(ClientProcess& proc, int idx)
: m_proc(proc), m_idx(idx)
{
    m_thr = std::thread(std::bind(&Worker::proc, this));
}

void ClientProcess::Worker::proc()
{
    ClientProcess::ThreadWorker.reset(this);

    char thrName[64];
    snprintf(thrName, 64, "HECL Client Worker %d", m_idx);
    logvisor::RegisterThreadName(thrName);

    while (m_proc.m_running)
    {
        std::unique_lock<std::mutex> lk(m_proc.m_mutex);
        if (!m_didInit)
        {
            m_proc.m_initCv.notify_one();
            m_didInit = true;
        }
        while (m_proc.m_running && m_proc.m_pendingQueue.size())
        {
            std::shared_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
            ++m_proc.m_inProgress;
            m_proc.m_pendingQueue.pop_front();
            lk.unlock();
            trans->run(m_blendTok);
            lk.lock();
            m_proc.m_completedQueue.push_back(std::move(trans));
            --m_proc.m_inProgress;
        }
        m_proc.m_waitCv.notify_one();
        if (!m_proc.m_running)
            break;
        m_proc.m_cv.wait(lk);
    }
    m_blendTok.shutdown();
}

ClientProcess::ClientProcess(int verbosityLevel, bool fast, bool force)
: m_verbosity(verbosityLevel), m_fast(fast), m_force(force)
{
#if HECL_MULTIPROCESSOR
    const int cpuCount = GetCPUCount();
#else
    constexpr int cpuCount = 1;
#endif
    m_workers.reserve(cpuCount);
    for (int i=0 ; i<cpuCount ; ++i)
    {
        std::unique_lock<std::mutex> lk(m_mutex);
        m_workers.emplace_back(*this, m_workers.size());
        m_initCv.wait(lk);
    }
}

std::shared_ptr<const ClientProcess::BufferTransaction>
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
                                    size_t maxLen, size_t offset)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

std::shared_ptr<const ClientProcess::CookTransaction>
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto ret = std::make_shared<CookTransaction>(*this, path, spec);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

std::shared_ptr<const ClientProcess::LambdaTransaction>
ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func));
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok)
{
    if (spec->canCook(path, btok))
    {
        const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
        if (specEnt)
        {
            hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
            if (m_fast)
                cooked = cooked.getWithExtension(_S(".fast"));
            cooked.makeDirChain(false);
            if (m_force || cooked.getPathType() == ProjectPath::Type::None ||
                path.getModtime() > cooked.getModtime())
            {
                if (path.getAuxInfo().empty())
                    LogModule.report(logvisor::Info, _S("Cooking %s"),
                                     path.getRelativePath().data());
                else
                    LogModule.report(logvisor::Info, _S("Cooking %s|%s"),
                                     path.getRelativePath().data(),
                                     path.getAuxInfo().data());
                spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
            }
            return true;
        }
    }
    return false;
}

void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    queue.swap(m_completedQueue);
}

void ClientProcess::waitUntilComplete()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    while (isBusy())
        m_waitCv.wait(lk);
}

void ClientProcess::shutdown()
{
    if (!m_running)
        return;
    std::unique_lock<std::mutex> lk(m_mutex);
    m_pendingQueue.clear();
    m_running = false;
    m_cv.notify_all();
    lk.unlock();
    for (Worker& worker : m_workers)
        if (worker.m_thr.joinable())
            worker.m_thr.join();
}

}
<|endoftext|>


<commit_before>#ifndef LIBICS3_ICS3_ID_H_
#define LIBICS3_ICS3_ID_H_

#include<stdexcept>

namespace ics {
  class ID {
  public:
    constexpr ID(uint8_t); // ID is non explicit constructor because only do check limit
    constexpr uint8_t get() const noexcept;
    constexpr operator uint8_t() const noexcept;
  private:
    const uint8_t data;
  };

  constexpr ID::ID(uint8_t id)
  : data {id < 32 ? id : throw std::invalid_argument {"invalid ID: must be 0 <= id <= 31"}}
  {}

  constexpr uint8_t ID::get() const noexcept {
    return data;
  }

  constexpr ID::operator uint8_t() const noexcept {
    return get();
  }
}

#endif // LIBICS3_ICS3_ID_H_
<commit_msg>Add alias of id data<commit_after>#ifndef LIBICS3_ICS3_ID_H_
#define LIBICS3_ICS3_ID_H_

#include<stdexcept>

namespace ics {
  class ID {
  public:
    using type = uint8_t;
    constexpr ID(type); // ID is non explicit constructor because only do check limit
    constexpr type get() const noexcept;
    constexpr operator type() const noexcept;
  private:
    const type data;
  };

  constexpr ID::ID(type id)
  : data {id < 32 ? id : throw std::invalid_argument {"invalid ID: must be 0 <= id <= 31"}}
  {}

  constexpr ID::type ID::get() const noexcept {
    return data;
  }

  constexpr ID::operator type() const noexcept {
    return get();
  }
}

#endif // LIBICS3_ICS3_ID_H_
<|endoftext|>


<commit_before>#ifndef LIBICS3_ICS3_ID_H_
#define LIBICS3_ICS3_ID_H_

#include<stdexcept>

namespace ics {
  class ID {
  public:
    constexpr ID(unsigned char);
    constexpr operator unsigned char() const noexcept;
    constexpr unsigned char get() const noexcept;
  private:
    const unsigned char data;
  };

  constexpr ID::ID(unsigned char id)
  : data {id < 32 ? id : throw std::invalid_argument {"Too big arguments"}}
  {}

  constexpr ID::operator unsigned char() const noexcept {
    return data;
  }

  constexpr unsigned char ID::get() const noexcept {
    return data;
  }
}

#endif // LIBICS3_ICS3_ID_H_
<commit_msg>Refactor get and unsigned char() method on ID<commit_after>#ifndef LIBICS3_ICS3_ID_H_
#define LIBICS3_ICS3_ID_H_

#include<stdexcept>

namespace ics {
  class ID {
  public:
    constexpr ID(unsigned char);
    constexpr unsigned char get() const noexcept;
    constexpr operator unsigned char() const noexcept;
  private:
    const unsigned char data;
  };

  constexpr ID::ID(unsigned char id)
  : data {id < 32 ? id : throw std::invalid_argument {"Too big arguments"}}
  {}

  constexpr unsigned char ID::get() const noexcept {
    return data;
  }

  constexpr ID::operator unsigned char() const noexcept {
    return get();
  }
}

#endif // LIBICS3_ICS3_ID_H_
<|endoftext|>


<commit_before>// Copyright 2017, Dawid Kurek, <dawikur@gmail.com>

#ifndef INCLUDE_LEL_BOX_HPP_
#define INCLUDE_LEL_BOX_HPP_

#include <type_traits>

namespace LeL {

template <class Type, Type... Tokens>
struct Box {
 private:
  template <Type... Values>
  using Self = Box<Type, Values...>;

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl {
    static_assert(sizeof...(Tail) != 0, "Index not found");
  };

  template <class Merged, class Left, class Right>
  struct MergeImpl;

 public:
  template <class>
  struct Merge;

  template <Type... NewTokens>
  struct Merge<Self<NewTokens...>> {
    using Result =
      typename MergeImpl<Self<>, Self<Tokens...>, Self<NewTokens...>>::Result;
  };

  template <Type       Token>
  static constexpr int IndexOf() noexcept {
    return IndexOfImpl<0, Token, Tokens...>::Result::value;
  }

  template <Type... NewTokens>
  using IndexesOf = Box<int, (IndexOf<NewTokens>())...>;

 private:
  template <int Index, Type Token, Type Head, Type... Tail>
  struct IndexOfImpl<Index, Token, Head, Tail...>
    : public IndexOfImpl<Index + 1, Token, Tail...> {};

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl<Index, Token, Token, Tail...> {
    using Result = std::integral_constant<int, Index>;
  };

  // Finish: only left
  template <Type... Merged, Type... Left>
  struct MergeImpl<Self<Merged...>, Self<Left...>, Self<>> {
    using Result = Self<Merged..., Left...>;
  };

  // Finish: only right
  template <Type... Merged, Type... Right>
  struct MergeImpl<Self<Merged...>, Self<>, Self<Right...>> {
    using Result = Self<Merged..., Right...>;
  };

  // Finish: nothing left to do
  template <Type... Merged>
  struct MergeImpl<Self<Merged...>, Self<>, Self<>> {
    using Result = Self<Merged...>;
  };

  // Heads are the same in Left and Right
  template <Type... Merged,
            Type Head,
            Type... TailL,
            Type... TailR>
  struct MergeImpl<Self<Merged...>,
                   Self<Head, TailL...>,
                   Self<Head, TailR...>>
    : public MergeImpl<Self<Merged..., Head>,
                       Self<TailL...>,
                       Self<TailR...>> {};

  template <Type Left, Type Right>
  static constexpr Type const Lower = Left < Right ? Left : Right;

  template <class, Type Condition>
  struct Choose;

  // Compare first elements from Left and Right
  template <Type... Merged,
            Type HeadL,
            Type... TailL,
            Type HeadR,
            Type... TailR>
  struct MergeImpl<Self<Merged...>,
                   Self<HeadL, TailL...>,
                   Self<HeadR, TailR...>>
    : public MergeImpl<Self<Merged..., Lower<HeadL, HeadR>>,
                       typename Choose<bool, (HeadL < HeadR)>::
                         template From<Self<TailL...>, Self<HeadL, TailL...>>,
                       typename Choose<bool, (HeadL < HeadR)>::
                         template From<Self<HeadR, TailR...>, Self<TailR...>>> {
  };

  template <class Dummy>
  struct Choose<Dummy, false> {
    template <class, class False>
    using From = False;
  };

  template <class Dummy>
  struct Choose<Dummy, true> {
    template <class True, class>
    using From = True;
  };

  // TODO: 2017-07-10 check if sequences are sored & unique
};

template <class Left, class Right>
using Merge = typename Left::template Merge<Right>::Result;

}  // namespace LeL

#endif  // INCLUDE_LEL_BOX_HPP_
<commit_msg>Try to fix VS compilation<commit_after>// Copyright 2017, Dawid Kurek, <dawikur@gmail.com>

#ifndef INCLUDE_LEL_BOX_HPP_
#define INCLUDE_LEL_BOX_HPP_

#include <type_traits>

namespace LeL {

template <class Type, Type... Tokens>
struct Box {
 private:
  template <Type... Values>
  using Self = Box<Type, Values...>;

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl {
    static_assert(sizeof...(Tail) != 0, "Index not found");
  };

  template <class Merged, class Left, class Right>
  struct MergeImpl;

 public:
  template <class>
  struct Merge;

  template <Type... NewTokens>
  struct Merge<Self<NewTokens...>> {
    using Result =
      typename MergeImpl<Self<>, Self<Tokens...>, Self<NewTokens...>>::Result;
  };

  template <Type       Token>
  static constexpr int IndexOf() noexcept {
    return IndexOfImpl<0, Token, Tokens...>::Result::value;
  }

  template <Type... NewTokens>
  using IndexesOf = Box<int, (IndexOf<NewTokens>())...>;

 private:
  template <int Index, Type Token, Type Head, Type... Tail>
  struct IndexOfImpl<Index, Token, Head, Tail...>
    : public IndexOfImpl<Index + 1, Token, Tail...> {};

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl<Index, Token, Token, Tail...> {
    using Result = std::integral_constant<int, Index>;
  };

  // Finish: only left
  template <Type... Merged, Type... Left>
  struct MergeImpl<Self<Merged...>, Self<Left...>, Self<>> {
    using Result = Self<Merged..., Left...>;
  };

  // Finish: only right
  template <Type... Merged, Type... Right>
  struct MergeImpl<Self<Merged...>, Self<>, Self<Right...>> {
    using Result = Self<Merged..., Right...>;
  };

  // Finish: nothing left to do
  template <Type... Merged>
  struct MergeImpl<Self<Merged...>, Self<>, Self<>> {
    using Result = Self<Merged...>;
  };

  // Heads are the same in Left and Right
  template <Type... Merged,
            Type Head,
            Type... TailL,
            Type... TailR>
  struct MergeImpl<Self<Merged...>,
                   Self<Head, TailL...>,
                   Self<Head, TailR...>>
    : public MergeImpl<Self<Merged..., Head>,
                       Self<TailL...>,
                       Self<TailR...>> {};

  template <Type Left, Type Right>
  static constexpr Type const Lower = Left < Right ? Left : Right;

  template <class, bool>
  struct Choose;

  // Compare first elements from Left and Right
  template <Type... Merged,
            Type HeadL,
            Type... TailL,
            Type HeadR,
            Type... TailR>
  struct MergeImpl<Self<Merged...>,
                   Self<HeadL, TailL...>,
                   Self<HeadR, TailR...>>
    : public MergeImpl<Self<Merged..., Lower<HeadL, HeadR>>,
                       typename Choose<bool, (HeadL < HeadR)>::
                         template From<Self<TailL...>, Self<HeadL, TailL...>>,
                       typename Choose<bool, (HeadL < HeadR)>::
                         template From<Self<HeadR, TailR...>, Self<TailR...>>> {
  };

  template <class Dummy>
  struct Choose<Dummy, false> {
    template <class, class False>
    using From = False;
  };

  template <class Dummy>
  struct Choose<Dummy, true> {
    template <class True, class>
    using From = True;
  };

  // TODO: 2017-07-10 check if sequences are sored & unique
};

template <class Left, class Right>
using Merge = typename Left::template Merge<Right>::Result;

}  // namespace LeL

#endif  // INCLUDE_LEL_BOX_HPP_
<|endoftext|>


<commit_before>//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayNULL.cpp:
//    Implements the class methods for DisplayNULL.
//

#include "libANGLE/renderer/null/DisplayNULL.h"

#include "common/debug.h"

#include "libANGLE/renderer/null/ContextNULL.h"
#include "libANGLE/renderer/null/DeviceNULL.h"
#include "libANGLE/renderer/null/ImageNULL.h"
#include "libANGLE/renderer/null/SurfaceNULL.h"

namespace rx
{

DisplayNULL::DisplayNULL(const egl::DisplayState &state) : DisplayImpl(state) {}

DisplayNULL::~DisplayNULL() {}

egl::Error DisplayNULL::initialize(egl::Display *display)
{
    constexpr size_t kMaxTotalAllocationSize = 1 << 28;  // 256MB
    mAllocationTracker.reset(new AllocationTrackerNULL(kMaxTotalAllocationSize));

    return egl::NoError();
}

void DisplayNULL::terminate()
{
    mAllocationTracker.reset();
}

egl::Error DisplayNULL::makeCurrent(egl::Surface *drawSurface,
                                    egl::Surface *readSurface,
                                    gl::Context *context)
{
    return egl::NoError();
}

egl::ConfigSet DisplayNULL::generateConfigs()
{
    egl::Config config;
    config.renderTargetFormat    = GL_RGBA8;
    config.depthStencilFormat    = GL_DEPTH24_STENCIL8;
    config.bufferSize            = 32;
    config.redSize               = 8;
    config.greenSize             = 8;
    config.blueSize              = 8;
    config.alphaSize             = 8;
    config.alphaMaskSize         = 0;
    config.bindToTextureRGB      = EGL_TRUE;
    config.bindToTextureRGBA     = EGL_TRUE;
    config.colorBufferType       = EGL_RGB_BUFFER;
    config.configCaveat          = EGL_NONE;
    config.conformant            = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
    config.depthSize             = 24;
    config.level                 = 0;
    config.matchNativePixmap     = EGL_NONE;
    config.maxPBufferWidth       = 0;
    config.maxPBufferHeight      = 0;
    config.maxPBufferPixels      = 0;
    config.maxSwapInterval       = 1;
    config.minSwapInterval       = 1;
    config.nativeRenderable      = EGL_TRUE;
    config.nativeVisualID        = 0;
    config.nativeVisualType      = EGL_NONE;
    config.renderableType        = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
    config.sampleBuffers         = 0;
    config.samples               = 0;
    config.stencilSize           = 8;
    config.surfaceType           = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
    config.optimalOrientation    = 0;
    config.transparentType       = EGL_NONE;
    config.transparentRedValue   = 0;
    config.transparentGreenValue = 0;
    config.transparentBlueValue  = 0;

    egl::ConfigSet configSet;
    configSet.add(config);
    return configSet;
}

bool DisplayNULL::testDeviceLost()
{
    return false;
}

egl::Error DisplayNULL::restoreLostDevice(const egl::Display *display)
{
    return egl::NoError();
}

bool DisplayNULL::isValidNativeWindow(EGLNativeWindowType window) const
{
    return true;
}

std::string DisplayNULL::getVendorString() const
{
    return "NULL";
}

DeviceImpl *DisplayNULL::createDevice()
{
    return new DeviceNULL();
}

egl::Error DisplayNULL::waitClient(const gl::Context *context)
{
    return egl::NoError();
}

egl::Error DisplayNULL::waitNative(const gl::Context *context, EGLint engine)
{
    return egl::NoError();
}

gl::Version DisplayNULL::getMaxSupportedESVersion() const
{
    return gl::Version(3, 2);
}

gl::Version DisplayNULL::getMaxConformantESVersion() const
{
    return getMaxSupportedESVersion();
}

SurfaceImpl *DisplayNULL::createWindowSurface(const egl::SurfaceState &state,
                                              EGLNativeWindowType window,
                                              const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

SurfaceImpl *DisplayNULL::createPbufferSurface(const egl::SurfaceState &state,
                                               const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

SurfaceImpl *DisplayNULL::createPbufferFromClientBuffer(const egl::SurfaceState &state,
                                                        EGLenum buftype,
                                                        EGLClientBuffer buffer,
                                                        const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

SurfaceImpl *DisplayNULL::createPixmapSurface(const egl::SurfaceState &state,
                                              NativePixmapType nativePixmap,
                                              const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

ImageImpl *DisplayNULL::createImage(const egl::ImageState &state,
                                    const gl::Context *context,
                                    EGLenum target,
                                    const egl::AttributeMap &attribs)
{
    return new ImageNULL(state);
}

rx::ContextImpl *DisplayNULL::createContext(const gl::State &state,
                                            gl::ErrorSet *errorSet,
                                            const egl::Config *configuration,
                                            const gl::Context *shareContext,
                                            const egl::AttributeMap &attribs)
{
    return new ContextNULL(state, errorSet, mAllocationTracker.get());
}

StreamProducerImpl *DisplayNULL::createStreamProducerD3DTexture(
    egl::Stream::ConsumerType consumerType,
    const egl::AttributeMap &attribs)
{
    UNIMPLEMENTED();
    return nullptr;
}

ShareGroupImpl *DisplayNULL::createShareGroup()
{
    return new ShareGroupNULL();
}

void DisplayNULL::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
    outExtensions->createContextRobustness            = true;
    outExtensions->postSubBuffer                      = true;
    outExtensions->createContext                      = true;
    outExtensions->deviceQuery                        = true;
    outExtensions->image                              = true;
    outExtensions->imageBase                          = true;
    outExtensions->glTexture2DImage                   = true;
    outExtensions->glTextureCubemapImage              = true;
    outExtensions->glTexture3DImage                   = true;
    outExtensions->glRenderbufferImage                = true;
    outExtensions->getAllProcAddresses                = true;
    outExtensions->flexibleSurfaceCompatibility       = true;
    outExtensions->directComposition                  = true;
    outExtensions->createContextNoError               = true;
    outExtensions->createContextWebGLCompatibility    = true;
    outExtensions->createContextBindGeneratesResource = true;
    outExtensions->swapBuffersWithDamage              = true;
    outExtensions->pixelFormatFloat                   = true;
    outExtensions->surfacelessContext                 = true;
    outExtensions->displayTextureShareGroup           = true;
    outExtensions->createContextClientArrays          = true;
    outExtensions->programCacheControl                = true;
    outExtensions->robustResourceInitialization       = true;
}

void DisplayNULL::generateCaps(egl::Caps *outCaps) const
{
    outCaps->textureNPOT = true;
}

}  // namespace rx
<commit_msg>Support EGL_ANGLE_display_semaphore_share_group for DisplayNULL<commit_after>//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayNULL.cpp:
//    Implements the class methods for DisplayNULL.
//

#include "libANGLE/renderer/null/DisplayNULL.h"

#include "common/debug.h"

#include "libANGLE/renderer/null/ContextNULL.h"
#include "libANGLE/renderer/null/DeviceNULL.h"
#include "libANGLE/renderer/null/ImageNULL.h"
#include "libANGLE/renderer/null/SurfaceNULL.h"

namespace rx
{

DisplayNULL::DisplayNULL(const egl::DisplayState &state) : DisplayImpl(state) {}

DisplayNULL::~DisplayNULL() {}

egl::Error DisplayNULL::initialize(egl::Display *display)
{
    constexpr size_t kMaxTotalAllocationSize = 1 << 28;  // 256MB
    mAllocationTracker.reset(new AllocationTrackerNULL(kMaxTotalAllocationSize));

    return egl::NoError();
}

void DisplayNULL::terminate()
{
    mAllocationTracker.reset();
}

egl::Error DisplayNULL::makeCurrent(egl::Surface *drawSurface,
                                    egl::Surface *readSurface,
                                    gl::Context *context)
{
    return egl::NoError();
}

egl::ConfigSet DisplayNULL::generateConfigs()
{
    egl::Config config;
    config.renderTargetFormat    = GL_RGBA8;
    config.depthStencilFormat    = GL_DEPTH24_STENCIL8;
    config.bufferSize            = 32;
    config.redSize               = 8;
    config.greenSize             = 8;
    config.blueSize              = 8;
    config.alphaSize             = 8;
    config.alphaMaskSize         = 0;
    config.bindToTextureRGB      = EGL_TRUE;
    config.bindToTextureRGBA     = EGL_TRUE;
    config.colorBufferType       = EGL_RGB_BUFFER;
    config.configCaveat          = EGL_NONE;
    config.conformant            = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
    config.depthSize             = 24;
    config.level                 = 0;
    config.matchNativePixmap     = EGL_NONE;
    config.maxPBufferWidth       = 0;
    config.maxPBufferHeight      = 0;
    config.maxPBufferPixels      = 0;
    config.maxSwapInterval       = 1;
    config.minSwapInterval       = 1;
    config.nativeRenderable      = EGL_TRUE;
    config.nativeVisualID        = 0;
    config.nativeVisualType      = EGL_NONE;
    config.renderableType        = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
    config.sampleBuffers         = 0;
    config.samples               = 0;
    config.stencilSize           = 8;
    config.surfaceType           = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
    config.optimalOrientation    = 0;
    config.transparentType       = EGL_NONE;
    config.transparentRedValue   = 0;
    config.transparentGreenValue = 0;
    config.transparentBlueValue  = 0;

    egl::ConfigSet configSet;
    configSet.add(config);
    return configSet;
}

bool DisplayNULL::testDeviceLost()
{
    return false;
}

egl::Error DisplayNULL::restoreLostDevice(const egl::Display *display)
{
    return egl::NoError();
}

bool DisplayNULL::isValidNativeWindow(EGLNativeWindowType window) const
{
    return true;
}

std::string DisplayNULL::getVendorString() const
{
    return "NULL";
}

DeviceImpl *DisplayNULL::createDevice()
{
    return new DeviceNULL();
}

egl::Error DisplayNULL::waitClient(const gl::Context *context)
{
    return egl::NoError();
}

egl::Error DisplayNULL::waitNative(const gl::Context *context, EGLint engine)
{
    return egl::NoError();
}

gl::Version DisplayNULL::getMaxSupportedESVersion() const
{
    return gl::Version(3, 2);
}

gl::Version DisplayNULL::getMaxConformantESVersion() const
{
    return getMaxSupportedESVersion();
}

SurfaceImpl *DisplayNULL::createWindowSurface(const egl::SurfaceState &state,
                                              EGLNativeWindowType window,
                                              const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

SurfaceImpl *DisplayNULL::createPbufferSurface(const egl::SurfaceState &state,
                                               const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

SurfaceImpl *DisplayNULL::createPbufferFromClientBuffer(const egl::SurfaceState &state,
                                                        EGLenum buftype,
                                                        EGLClientBuffer buffer,
                                                        const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

SurfaceImpl *DisplayNULL::createPixmapSurface(const egl::SurfaceState &state,
                                              NativePixmapType nativePixmap,
                                              const egl::AttributeMap &attribs)
{
    return new SurfaceNULL(state);
}

ImageImpl *DisplayNULL::createImage(const egl::ImageState &state,
                                    const gl::Context *context,
                                    EGLenum target,
                                    const egl::AttributeMap &attribs)
{
    return new ImageNULL(state);
}

rx::ContextImpl *DisplayNULL::createContext(const gl::State &state,
                                            gl::ErrorSet *errorSet,
                                            const egl::Config *configuration,
                                            const gl::Context *shareContext,
                                            const egl::AttributeMap &attribs)
{
    return new ContextNULL(state, errorSet, mAllocationTracker.get());
}

StreamProducerImpl *DisplayNULL::createStreamProducerD3DTexture(
    egl::Stream::ConsumerType consumerType,
    const egl::AttributeMap &attribs)
{
    UNIMPLEMENTED();
    return nullptr;
}

ShareGroupImpl *DisplayNULL::createShareGroup()
{
    return new ShareGroupNULL();
}

void DisplayNULL::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
    outExtensions->createContextRobustness            = true;
    outExtensions->postSubBuffer                      = true;
    outExtensions->createContext                      = true;
    outExtensions->deviceQuery                        = true;
    outExtensions->image                              = true;
    outExtensions->imageBase                          = true;
    outExtensions->glTexture2DImage                   = true;
    outExtensions->glTextureCubemapImage              = true;
    outExtensions->glTexture3DImage                   = true;
    outExtensions->glRenderbufferImage                = true;
    outExtensions->getAllProcAddresses                = true;
    outExtensions->flexibleSurfaceCompatibility       = true;
    outExtensions->directComposition                  = true;
    outExtensions->createContextNoError               = true;
    outExtensions->createContextWebGLCompatibility    = true;
    outExtensions->createContextBindGeneratesResource = true;
    outExtensions->swapBuffersWithDamage              = true;
    outExtensions->pixelFormatFloat                   = true;
    outExtensions->surfacelessContext                 = true;
    outExtensions->displayTextureShareGroup           = true;
    outExtensions->displaySemaphoreShareGroup         = true;
    outExtensions->createContextClientArrays          = true;
    outExtensions->programCacheControl                = true;
    outExtensions->robustResourceInitialization       = true;
}

void DisplayNULL::generateCaps(egl::Caps *outCaps) const
{
    outCaps->textureNPOT = true;
}

}  // namespace rx
<|endoftext|>


<commit_before>#include "AnsiDisplay.hpp"
#include <iostream>
#include <random>

namespace
{
  void displayHeader(int length)
  {
    for(int i=0;i<=length*3+1;++i)
      std::cout << "-";
    std::cout << std::endl;
  }

  void displayRock()
  {
    std::cout << "\033[31m";
    std::cout << "/-\\";
  }

  void displaySpace()
  {
    thread_local std::random_device rd;
    thread_local std::mt19937 engine(rd());
    std::uniform_int_distribution<> die(0, 3);

    std::cout << "\033[32m";
    for(int i=0;i<3;++i)
    {
      auto r = die(engine);
      switch (r)
      {
      case 0:
        std::cout << ',';
        break;
      case 1:
        std::cout << '.';
        break;
      case 2:
        std::cout << "'"; break;
      case 3:
        std::cout << '~';
        break;
      default:
        std::cout << '.';
        break;
      }
    }
  }

  char displayType(lionheart::UnitType type)
  {
    switch (type)
    {
    case lionheart::CROWN:
      return '*';
    case lionheart::KNIGHT:
      return 'k';
    case lionheart::ARCHER:
      return 'a';
    case lionheart::INFANTRY:
      return 'i';
    default:
      break;
    }
    return '.';
  }

  char displayDirection(lionheart::Direction dir)
  {
    switch (dir)
    {
    case lionheart::Direction::NORTH:
      return '^';
    case lionheart::Direction::SOUTH:
      return 'v';
    case lionheart::Direction::WEST:
      return '<';
    case lionheart::Direction::EAST:
      return '>';
    default:
      break;
    }
    return '.';
  }

  void setUnitColor(lionheart::Blazon const& blazon)
  {
    switch(blazon.primary)
    {
    case lionheart::Color::ARGENT:
      std::cout << "\033[47;1m";
      break;
    case lionheart::Color::OR:
      std::cout << "\033[43;1m";
      break;
    case lionheart::Color::GULES:
      std::cout << "\033[41;1m";
      break;
    case lionheart::Color::VERT:
      std::cout << "\033[42;1m";
      break;
    case lionheart::Color::AZURE:
      std::cout << "\033[44;1m";
      break;
    case lionheart::Color::SABLE:
      std::cout << "\033[40;m";
      break;
    default:
      std::cout << "\033[0m";
    }
    switch (blazon.secondary)
    {
    case lionheart::Color::ARGENT:
      std::cout << "\033[37;1m";
      break;
    case lionheart::Color::OR:
      std::cout << "\033[33;1m";
      break;
    case lionheart::Color::GULES:
      std::cout << "\033[31;1m";
      break;
    case lionheart::Color::VERT:
      std::cout << "\033[32;1m";
      break;
    case lionheart::Color::AZURE:
      std::cout << "\033[34;1m";
      break;
    case lionheart::Color::SABLE:
      std::cout << "\033[30;1m";
      break;
    default:
      std::cout << "\033[0m";
    }
  }
  void displayUnit(lionheart::SituationReport::Thing const& thing,
                   lionheart::Blazon const& blazon)
  {
    setUnitColor(blazon);
    std::cout << displayType(thing.unit) << thing.hp << displayDirection(thing.direction);
    std::cout << "\033[0m";
  }

  void displayThing(lionheart::SituationReport::Thing const& thing,
                    lionheart::Blazon const& ally,
                    lionheart::Blazon const& enemy)
  {
    switch (thing.type)
    {
    case lionheart::SituationReport::ROCK:
      displayRock();
      break;
    case lionheart::SituationReport::SPACE:
      displaySpace();
      break;
    case lionheart::SituationReport::ALLY:
      displayUnit(thing, ally);
      break;
    case lionheart::SituationReport::ENEMY:
      displayUnit(thing, enemy);
      break;
    }
  }
}

void lionheart::AnsiDisplay::show(lionheart::SituationReport const& report, Blazon const& p1, Blazon const& p2)
{
  if (report.things.empty()) return;
  std::cout << p1.name << " v. " << p2.name << std::endl;

  displayHeader(report.things[0].size());
  for (auto&& row : report.things)
  {
    std::cout << '|';
    for (auto&& thing : row)
    {
      displayThing(thing, p1, p2);
    }
    // turn off color
    std::cout << "\033[0m";
    std::cout << '|' << std::endl;
  }
  displayHeader(report.things[0].size());
}
<commit_msg>simplify random dirt display<commit_after>#include "AnsiDisplay.hpp"
#include <iostream>
#include <random>

namespace
{
  void displayHeader(int length)
  {
    for(int i=0;i<=length*3+1;++i)
      std::cout << "-";
    std::cout << std::endl;
  }

  void displayRock()
  {
    std::cout << "\033[31m";
    std::cout << "/-\\";
  }

  void displaySpace()
  {
    thread_local std::random_device rd;
    thread_local std::mt19937 engine(rd());
    const int SIZE = 6;
    std::uniform_int_distribution<> die(0, SIZE-1);

    const char c[SIZE] = { ',', '.', '~', '`', '"', '\'' };
    std::cout << "\033[32m";
    for(int i=0;i<3;++i)
    {
      auto r = die(engine);
      std::cout << c[r];
    }
  }

  char displayType(lionheart::UnitType type)
  {
    switch (type)
    {
    case lionheart::CROWN:
      return '*';
    case lionheart::KNIGHT:
      return 'k';
    case lionheart::ARCHER:
      return 'a';
    case lionheart::INFANTRY:
      return 'i';
    default:
      break;
    }
    return '.';
  }

  char displayDirection(lionheart::Direction dir)
  {
    switch (dir)
    {
    case lionheart::Direction::NORTH:
      return '^';
    case lionheart::Direction::SOUTH:
      return 'v';
    case lionheart::Direction::WEST:
      return '<';
    case lionheart::Direction::EAST:
      return '>';
    default:
      break;
    }
    return '.';
  }

  void setUnitColor(lionheart::Blazon const& blazon)
  {
    switch(blazon.primary)
    {
    case lionheart::Color::ARGENT:
      std::cout << "\033[47;1m";
      break;
    case lionheart::Color::OR:
      std::cout << "\033[43;1m";
      break;
    case lionheart::Color::GULES:
      std::cout << "\033[41;1m";
      break;
    case lionheart::Color::VERT:
      std::cout << "\033[42;1m";
      break;
    case lionheart::Color::AZURE:
      std::cout << "\033[44;1m";
      break;
    case lionheart::Color::SABLE:
      std::cout << "\033[40;m";
      break;
    default:
      std::cout << "\033[0m";
    }
    switch (blazon.secondary)
    {
    case lionheart::Color::ARGENT:
      std::cout << "\033[37;1m";
      break;
    case lionheart::Color::OR:
      std::cout << "\033[33;1m";
      break;
    case lionheart::Color::GULES:
      std::cout << "\033[31;1m";
      break;
    case lionheart::Color::VERT:
      std::cout << "\033[32;1m";
      break;
    case lionheart::Color::AZURE:
      std::cout << "\033[34;1m";
      break;
    case lionheart::Color::SABLE:
      std::cout << "\033[30;1m";
      break;
    default:
      std::cout << "\033[0m";
    }
  }
  void displayUnit(lionheart::SituationReport::Thing const& thing,
                   lionheart::Blazon const& blazon)
  {
    setUnitColor(blazon);
    std::cout << displayType(thing.unit) << thing.hp << displayDirection(thing.direction);
    std::cout << "\033[0m";
  }

  void displayThing(lionheart::SituationReport::Thing const& thing,
                    lionheart::Blazon const& ally,
                    lionheart::Blazon const& enemy)
  {
    switch (thing.type)
    {
    case lionheart::SituationReport::ROCK:
      displayRock();
      break;
    case lionheart::SituationReport::SPACE:
      displaySpace();
      break;
    case lionheart::SituationReport::ALLY:
      displayUnit(thing, ally);
      break;
    case lionheart::SituationReport::ENEMY:
      displayUnit(thing, enemy);
      break;
    }
  }
}

void lionheart::AnsiDisplay::show(lionheart::SituationReport const& report, Blazon const& p1, Blazon const& p2)
{
  if (report.things.empty()) return;
  std::cout << p1.name << " v. " << p2.name << std::endl;

  displayHeader(report.things[0].size());
  for (auto&& row : report.things)
  {
    std::cout << '|';
    for (auto&& thing : row)
    {
      displayThing(thing, p1, p2);
    }
    // turn off color
    std::cout << "\033[0m";
    std::cout << '|' << std::endl;
  }
  displayHeader(report.things[0].size());
}
<|endoftext|>


<commit_before><commit_msg>coverity#1255387 help coverity out here wrt Division by zero<commit_after><|endoftext|>


<commit_before>#include "vector.h"
#include "MinIMU9.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <system_error>
#include <boost/program_options.hpp>

namespace opts = boost::program_options;

// TODO: see if we can switch from Eigen to boost for the math stuff
// TODO: print warning if accelerometer magnitude is not close to 1 when starting up

int millis()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (tv.tv_sec) * 1000 + (tv.tv_usec)/1000;
}

void print(matrix m)
{
    printf("%7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f",
           m(0,0), m(0,1), m(0,2),
           m(1,0), m(1,1), m(1,2),
           m(2,0), m(2,1), m(2,2));
}

void streamRawValues(IMU& imu)
{
    imu.enable();
    while(1)
    {
        imu.read();
        printf("%7d %7d %7d  %7d %7d %7d  %7d %7d %7d\n",
               imu.raw_m[0], imu.raw_m[1], imu.raw_m[2],
               imu.raw_a[0], imu.raw_a[1], imu.raw_a[2],
               imu.raw_g[0], imu.raw_g[1], imu.raw_g[2]
        );
        usleep(20*1000);
    }
}

matrix rotationFromCompass(const vector& acceleration, const vector& magnetic_field)
{
    vector up = acceleration;     // usually true
    vector east = magnetic_field.cross(up); // actual it's magnetic east
    vector north = up.cross(east);

    matrix rotationFromCompass;
    rotationFromCompass.row(0) = east;
    rotationFromCompass.row(1) = north;
    rotationFromCompass.row(2) = up;
    rotationFromCompass.row(0).normalize();
    rotationFromCompass.row(1).normalize();
    rotationFromCompass.row(2).normalize();

    return rotationFromCompass;
}

float heading(matrix rotation)
{
    // The board's x axis in earth coordinates.
    vector x = rotation.col(0);
    x.normalize();
    x(2) = 0;

    // 0 = east, pi/2 = north
    return atan2(x(1), x(0));
}

typedef void fuse_function(quaternion& rotation, float dt, const vector& angular_velocity,
                  const vector& acceleration, const vector& magnetic_field);

void fuse_compass_only(quaternion& rotation, float dt, const vector& angular_velocity,
  const vector& acceleration, const vector& magnetic_field)
{
    // Implicit conversion of rotation matrix to quaternion.
    rotation = rotationFromCompass(acceleration, magnetic_field);
}

// w is angular velocity in radiands per second.
// dt is the time.
void rotate(quaternion& rotation, const vector& w, float dt)
{
    // First order approximation of the quaternion representing this rotation.
    quaternion q = quaternion(1, w(0)*dt/2, w(1)*dt/2, w(2)*dt/2);
    rotation *= q;
    rotation.normalize();
}

void fuse_gyro_only(quaternion& rotation, float dt, const vector& angular_velocity,
  const vector& acceleration, const vector& magnetic_field)
{
    rotate(rotation, angular_velocity, dt);
}

void fuse_default(quaternion& rotation, float dt, const vector& angular_velocity,
  const vector& acceleration, const vector& magnetic_field)
{
    vector correction = vector(0, 0, 0);

    if (abs(acceleration.norm() - 1) <= 0.3)
    {
        // The magnetidude of acceleration is close to 1 g, so
        // it might be pointing up and we can do drift correction.

        const float correction_strength = 1;

        matrix rotationCompass = rotationFromCompass(acceleration, magnetic_field);
        matrix rotationMatrix = rotation.toRotationMatrix();

        correction = (
            rotationCompass.row(0).cross(rotationMatrix.row(0)) +
            rotationCompass.row(1).cross(rotationMatrix.row(1)) +
            rotationCompass.row(2).cross(rotationMatrix.row(2))
          ) * correction_strength;

    }

    rotate(rotation, angular_velocity + correction, dt);
}

void ahrs(IMU& imu, fuse_function * fuse_func)
{
    imu.loadCalibration();
    imu.enable();
    imu.measureOffsets();

    // The quaternion that can convert a vector in body coordinates
    // to ground coordinates when it its changed to a matrix.
    quaternion rotation = quaternion::Identity();

    int start = millis(); // truncate 64-bit return value
    while(1)
    {
        int last_start = start;
        start = millis();
        float dt = (start-last_start)/1000.0;
        if (dt < 0){ throw std::runtime_error("time went backwards"); }

        vector angular_velocity = imu.readGyro();
        vector acceleration = imu.readAcc();
        vector magnetic_field = imu.readMag();

        fuse_func(rotation, dt, angular_velocity, acceleration, magnetic_field);

        matrix r = rotation.toRotationMatrix();
        printf("%7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f  %7.4f %7.4f %7.4f  %7.4f %7.4f %7.4f\n",
               r(0,0), r(0,1), r(0,2),
               r(1,0), r(1,1), r(1,2),
               r(2,0), r(2,1), r(2,2),
               acceleration(0), acceleration(1), acceleration(2),
               magnetic_field(0), magnetic_field(1), magnetic_field(2));
        fflush(stdout);

        // Ensure that each iteration of the loop takes at least 20 ms.
        while(millis() - start < 20)
        {
            usleep(1000);
        }
    }
}

int main(int argc, char *argv[])
{
    try
    {
        opts::options_description desc("Allowed options");
        desc.add_options()
          ("help", "produce help message")
          ("compression", opts::value<int>(), "set compression level")
        ;
        opts::variables_map vm;
        opts::store(opts::parse_command_line(argc, argv, desc), vm);
        opts::notify(vm);

        MinIMU9 imu("/dev/i2c-0");

        imu.checkConnection();

        if (argc > 1)
        {
            const char * action = argv[1];
            if (0 == strcmp("raw", action))
            {
                streamRawValues(imu);
            }
            else if (0 == strcmp("gyro-only", action))
            {
                ahrs(imu, &fuse_gyro_only);
            }
            else if (0 == strcmp("compass-only", action))
            {
                ahrs(imu, &fuse_compass_only);
            }
            else
            {
                fprintf(stderr, "Unknown action '%s'.\n", action);
                return 3;
            }
        }
        else
        {
            ahrs(imu, &fuse_default);
        }
        return 0;
    }
    catch(const std::system_error & error)
    {
        std::string what = error.what();
        const std::error_code & code = error.code();
        std::cerr << "Error: " << what << "  " << code.message() << " (" << code << ")\n";
    }
    catch(const std::exception & error)
    {
        std::cerr << "Error: " << error.what() << '\n';
    }
    return 1;
}
<commit_msg>Use boost program_options to determine mode of operation.<commit_after>#include "vector.h"
#include "MinIMU9.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <system_error>
#include <boost/program_options.hpp>

namespace opts = boost::program_options;

// TODO: see if we can switch from Eigen to boost for the math stuff
// TODO: print warning if accelerometer magnitude is not close to 1 when starting up

int millis()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (tv.tv_sec) * 1000 + (tv.tv_usec)/1000;
}

void print(matrix m)
{
    printf("%7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f",
           m(0,0), m(0,1), m(0,2),
           m(1,0), m(1,1), m(1,2),
           m(2,0), m(2,1), m(2,2));
}

void streamRawValues(IMU& imu)
{
    imu.enable();
    while(1)
    {
        imu.read();
        printf("%7d %7d %7d  %7d %7d %7d  %7d %7d %7d\n",
               imu.raw_m[0], imu.raw_m[1], imu.raw_m[2],
               imu.raw_a[0], imu.raw_a[1], imu.raw_a[2],
               imu.raw_g[0], imu.raw_g[1], imu.raw_g[2]
        );
        usleep(20*1000);
    }
}

matrix rotationFromCompass(const vector& acceleration, const vector& magnetic_field)
{
    vector up = acceleration;     // usually true
    vector east = magnetic_field.cross(up); // actual it's magnetic east
    vector north = up.cross(east);

    matrix rotationFromCompass;
    rotationFromCompass.row(0) = east;
    rotationFromCompass.row(1) = north;
    rotationFromCompass.row(2) = up;
    rotationFromCompass.row(0).normalize();
    rotationFromCompass.row(1).normalize();
    rotationFromCompass.row(2).normalize();

    return rotationFromCompass;
}

float heading(matrix rotation)
{
    // The board's x axis in earth coordinates.
    vector x = rotation.col(0);
    x.normalize();
    x(2) = 0;

    // 0 = east, pi/2 = north
    return atan2(x(1), x(0));
}

typedef void fuse_function(quaternion& rotation, float dt, const vector& angular_velocity,
                  const vector& acceleration, const vector& magnetic_field);

void fuse_compass_only(quaternion& rotation, float dt, const vector& angular_velocity,
  const vector& acceleration, const vector& magnetic_field)
{
    // Implicit conversion of rotation matrix to quaternion.
    rotation = rotationFromCompass(acceleration, magnetic_field);
}

// w is angular velocity in radiands per second.
// dt is the time.
void rotate(quaternion& rotation, const vector& w, float dt)
{
    // First order approximation of the quaternion representing this rotation.
    quaternion q = quaternion(1, w(0)*dt/2, w(1)*dt/2, w(2)*dt/2);
    rotation *= q;
    rotation.normalize();
}

void fuse_gyro_only(quaternion& rotation, float dt, const vector& angular_velocity,
  const vector& acceleration, const vector& magnetic_field)
{
    rotate(rotation, angular_velocity, dt);
}

void fuse_default(quaternion& rotation, float dt, const vector& angular_velocity,
  const vector& acceleration, const vector& magnetic_field)
{
    vector correction = vector(0, 0, 0);

    if (abs(acceleration.norm() - 1) <= 0.3)
    {
        // The magnetidude of acceleration is close to 1 g, so
        // it might be pointing up and we can do drift correction.

        const float correction_strength = 1;

        matrix rotationCompass = rotationFromCompass(acceleration, magnetic_field);
        matrix rotationMatrix = rotation.toRotationMatrix();

        correction = (
            rotationCompass.row(0).cross(rotationMatrix.row(0)) +
            rotationCompass.row(1).cross(rotationMatrix.row(1)) +
            rotationCompass.row(2).cross(rotationMatrix.row(2))
          ) * correction_strength;

    }

    rotate(rotation, angular_velocity + correction, dt);
}

void ahrs(IMU& imu, fuse_function * fuse_func)
{
    imu.loadCalibration();
    imu.enable();
    imu.measureOffsets();

    // The quaternion that can convert a vector in body coordinates
    // to ground coordinates when it its changed to a matrix.
    quaternion rotation = quaternion::Identity();

    int start = millis(); // truncate 64-bit return value
    while(1)
    {
        int last_start = start;
        start = millis();
        float dt = (start-last_start)/1000.0;
        if (dt < 0){ throw std::runtime_error("time went backwards"); }

        vector angular_velocity = imu.readGyro();
        vector acceleration = imu.readAcc();
        vector magnetic_field = imu.readMag();

        fuse_func(rotation, dt, angular_velocity, acceleration, magnetic_field);

        matrix r = rotation.toRotationMatrix();
        printf("%7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f  %7.4f %7.4f %7.4f  %7.4f %7.4f %7.4f\n",
               r(0,0), r(0,1), r(0,2),
               r(1,0), r(1,1), r(1,2),
               r(2,0), r(2,1), r(2,2),
               acceleration(0), acceleration(1), acceleration(2),
               magnetic_field(0), magnetic_field(1), magnetic_field(2));
        fflush(stdout);

        // Ensure that each iteration of the loop takes at least 20 ms.
        while(millis() - start < 20)
        {
            usleep(1000);
        }
    }
}

std::pair<std::string, std::string> option_translator(const std::string& s)
{
    if (s == "--gyro-only")
    {
        return std::make_pair("mode", "gyro-only");
    }
    else if (s == "--compass-only")
    {
        return std::make_pair("mode", "compass-only");
    }
    else if (s == "--raw")
    {
        return std::make_pair("mode", "raw");
    }
    else
    {
        return std::make_pair(std::string(), std::string());
    }
}

int main(int argc, char *argv[])
{
    try
    {
        std::string mode;
        opts::options_description desc("Allowed options");
        desc.add_options()
            ("help", "produce help message")
            ("mode", opts::value<std::string>(&mode)->default_value("normal"),
             "normal (default): Fuse compass and gyro.\n"
             "gyro-only:  Use only gyro (drifts).\n"
             "compass-only:  Use only compass (noisy).\n"
             "raw: Just print raw values from sensors.")
            ;
        opts::variables_map vm;
        opts::store(opts::command_line_parser(argc, argv).options(desc).extra_parser(option_translator).run(), vm);
        opts::notify(vm);

        if(vm.count("help"))
        {
            std::cout << desc << std::endl;
            return 0;
        }

        MinIMU9 imu("/dev/i2c-0");

        imu.checkConnection();

        if (mode == "raw")
        {
            streamRawValues(imu);
        }
        else if (mode == "gyro-only")
        {
            ahrs(imu, &fuse_gyro_only);
        }
        else if (mode == "compass-only")
        {
            ahrs(imu, &fuse_compass_only);
        }
        else if (mode == "normal")
        {
            ahrs(imu, &fuse_default);
        }
        else
        {
            std::cerr << "Unknown mode '" << mode << "'" << std::endl;
            return 1;
        }
        return 0;
    }
    catch(const std::system_error & error)
    {
        auto what = error.what();
        const std::error_code & code = error.code();
        std::cerr << "Error: " << what << "  " << code.message() << " (" << code << ")" << std::endl;
        return 2;
    }
    catch(const opts::multiple_occurrences & error)
    {
        std::cerr << "Error: " << error.what() << " of " << error.get_option_name() << " option." << std::endl;
        return 1;
    }
    catch(const std::exception & error)
    {
        std::cerr << "Error: " << error.what() << std::endl;
        return 9;
    }
}
<|endoftext|>


<commit_before>/*
 * xml_preferences_loader.cpp
 *
 * Author: Ming Tsang
 * Copyright (c) 2014 Ming Tsang
 * Refer to LICENSE for details
 */

#include <cstring>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>

#include <tinyxml2.h>

#include "libutils/log.h"
#include "libutils/io/preferences.h"
#include "libutils/io/xml_preferences_loader.h"
#include "libutils/str/encode_utils.h"
#include "libutils/str/str_utils.h"

using namespace std;

#define LU_NS_TAG "utils::io::"
#define LU_TAG LU_NS_TAG "XmlPreferencesLoader::"

namespace utils
{
namespace io
{

namespace
{

template<typename T>
struct PrimitiveSerializer
{
	static string Serialize(const T value);
	static T Unserialize(const string &value);
};

template<>
string PrimitiveSerializer<bool>::Serialize(const bool value)
{
	return value ? "true" : "false";
}

template<typename T>
string PrimitiveSerializer<T>::Serialize(const T value)
{
	return str::StrUtils::Concat(value);
}

template<>
bool PrimitiveSerializer<bool>::Unserialize(const string &value)
{
	return (value == "true");
}

template<typename T>
T PrimitiveSerializer<T>::Unserialize(const string &value)
{
	stringstream ss(value);
	T v;
	ss >> v;
	return v;
}

inline void PutPrimitive(const wstring &key, const bool v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutBool(key, v);
}

inline void PutPrimitive(const wstring &key, const float v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutFloat(key, v);
}

inline void PutPrimitive(const wstring &key, const int v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutInt(key, v);
}

inline void PutPrimitive(const wstring &key, const long v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutLong(key, v);
}

class Impl
{
public:
	static bool Load(const unique_ptr<Preferences::Editor> &edit, istream *read);

private:
	template<typename T>
	static bool LoadPrimitive(const tinyxml2::XMLElement *element,
			const unique_ptr<Preferences::Editor> &edit);
	static bool LoadString(const tinyxml2::XMLElement *element,
			const unique_ptr<Preferences::Editor> &edit);
	static bool LoadStringSet(const tinyxml2::XMLElement *element,
			const unique_ptr<Preferences::Editor> &edit);
};

bool Impl::Load(const unique_ptr<Preferences::Editor> &edit, istream *read)
{
	using namespace tinyxml2;

	string content{istreambuf_iterator<char>(*read),
			istreambuf_iterator<char>()};
	XMLDocument doc;
	if (doc.Parse(content.c_str()) != XML_NO_ERROR)
	{
		LU_LOG_E(LU_TAG "Load", "Failed while Parse");
		return false;
	}

	auto map = doc.FirstChildElement("map");
	if (!map)
	{
		LU_LOG_E(LU_TAG "Load", "map not found");
		return false;
	}

	for (auto element = map->FirstChildElement(); element;
			element = element->NextSiblingElement())
	{
		const char *type = element->Value();
		if (!strcmp(type, "boolean") && !LoadPrimitive<bool>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading bool");
		}
		else if (!strcmp(type, "float") && !LoadPrimitive<float>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading float");
		}
		else if (!strcmp(type, "int") && !LoadPrimitive<int>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading int");
		}
		else if (!strcmp(type, "long") && !LoadPrimitive<long>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading long");
		}
		else if (!strcmp(type, "string") && !LoadString(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading string");
		}
		else if (!strcmp(type, "set") && !LoadStringSet(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading string set");
		}
	}
	edit->CommitAndInvalidate();
	return true;
}

template<typename T>
bool Impl::LoadPrimitive(const tinyxml2::XMLElement *element,
		const unique_ptr<Preferences::Editor> &edit)
{
	wstring key;
	T value;
	bool is_has_value = false;

	for (auto attr = element->FirstAttribute(); attr; attr = attr->Next())
	{
		const char *name = attr->Name();
		if (!strcmp(name, "name"))
		{
			key = str::EncodeUtils::U8ToU16(attr->Value());
		}
		else if (!strcmp(name, "value"))
		{
			value = PrimitiveSerializer<T>::Unserialize(attr->Value());
			is_has_value = true;
		}
	}

	if (!key.empty() && is_has_value)
	{
		PutPrimitive(key, value, edit);
		return true;
	}
	else
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid key/value");
		return false;
	}
}

bool Impl::LoadString(const tinyxml2::XMLElement *element,
		const unique_ptr<Preferences::Editor> &edit)
{
	auto attr = element->FindAttribute("name");
	const char *text = element->GetText();
	if (!attr || !text)
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid node");
		return false;
	}

	wstring key = str::EncodeUtils::U8ToU16(attr->Value());
	wstring textStr = str::EncodeUtils::U8ToU16(text);
	if (!key.empty())
	{
		edit->PutString(key, std::move(textStr));
		return true;
	}
	else
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid key");
		return false;
	}
}

bool Impl::LoadStringSet(const tinyxml2::XMLElement *element,
		const unique_ptr<Preferences::Editor> &edit)
{
	auto attr = element->FindAttribute("name");
	if (!attr)
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid node");
		return false;
	}
	wstring key = str::EncodeUtils::U8ToU16(attr->Value());
	if (key.empty())
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid key");
		return false;
	}

	vector<wstring> set;
	for (auto child = element->FirstChildElement(); child;
			child = child->NextSiblingElement())
	{
		if (!strcmp(child->Name(), "string"))
		{
			const char *text = child->GetText();
			if (text)
			{
				set.push_back(str::EncodeUtils::U8ToU16(text));
			}
		}
	}
	edit->PutStringSet(key, std::move(set));
	return true;
}

}

XmlPreferencesLoader::XmlPreferencesLoader(std::istream *read)
		: m_read(read)
{}

XmlPreferencesLoader::~XmlPreferencesLoader()
{}

bool XmlPreferencesLoader::Load(const unique_ptr<Preferences::Editor> &edit)
{
	return Impl::Load(edit, m_read);
}

}
}
<commit_msg>Also initialize PODs<commit_after>/*
 * xml_preferences_loader.cpp
 *
 * Author: Ming Tsang
 * Copyright (c) 2014 Ming Tsang
 * Refer to LICENSE for details
 */

#include <cstring>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>

#include <tinyxml2.h>

#include "libutils/log.h"
#include "libutils/io/preferences.h"
#include "libutils/io/xml_preferences_loader.h"
#include "libutils/str/encode_utils.h"
#include "libutils/str/str_utils.h"

using namespace std;

#define LU_NS_TAG "utils::io::"
#define LU_TAG LU_NS_TAG "XmlPreferencesLoader::"

namespace utils
{
namespace io
{

namespace
{

template<typename T>
struct PrimitiveSerializer
{
	static string Serialize(const T value);
	static T Unserialize(const string &value);
};

template<>
string PrimitiveSerializer<bool>::Serialize(const bool value)
{
	return value ? "true" : "false";
}

template<typename T>
string PrimitiveSerializer<T>::Serialize(const T value)
{
	return str::StrUtils::Concat(value);
}

template<>
bool PrimitiveSerializer<bool>::Unserialize(const string &value)
{
	return (value == "true");
}

template<typename T>
T PrimitiveSerializer<T>::Unserialize(const string &value)
{
	stringstream ss(value);
	T v;
	ss >> v;
	return v;
}

inline void PutPrimitive(const wstring &key, const bool v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutBool(key, v);
}

inline void PutPrimitive(const wstring &key, const float v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutFloat(key, v);
}

inline void PutPrimitive(const wstring &key, const int v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutInt(key, v);
}

inline void PutPrimitive(const wstring &key, const long v,
		const unique_ptr<Preferences::Editor> &edit)
{
	edit->PutLong(key, v);
}

class Impl
{
public:
	static bool Load(const unique_ptr<Preferences::Editor> &edit, istream *read);

private:
	template<typename T>
	static bool LoadPrimitive(const tinyxml2::XMLElement *element,
			const unique_ptr<Preferences::Editor> &edit);
	static bool LoadString(const tinyxml2::XMLElement *element,
			const unique_ptr<Preferences::Editor> &edit);
	static bool LoadStringSet(const tinyxml2::XMLElement *element,
			const unique_ptr<Preferences::Editor> &edit);
};

bool Impl::Load(const unique_ptr<Preferences::Editor> &edit, istream *read)
{
	using namespace tinyxml2;

	string content{istreambuf_iterator<char>(*read),
			istreambuf_iterator<char>()};
	XMLDocument doc;
	if (doc.Parse(content.c_str()) != XML_NO_ERROR)
	{
		LU_LOG_E(LU_TAG "Load", "Failed while Parse");
		return false;
	}

	auto map = doc.FirstChildElement("map");
	if (!map)
	{
		LU_LOG_E(LU_TAG "Load", "map not found");
		return false;
	}

	for (auto element = map->FirstChildElement(); element;
			element = element->NextSiblingElement())
	{
		const char *type = element->Value();
		if (!strcmp(type, "boolean") && !LoadPrimitive<bool>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading bool");
		}
		else if (!strcmp(type, "float") && !LoadPrimitive<float>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading float");
		}
		else if (!strcmp(type, "int") && !LoadPrimitive<int>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading int");
		}
		else if (!strcmp(type, "long") && !LoadPrimitive<long>(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading long");
		}
		else if (!strcmp(type, "string") && !LoadString(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading string");
		}
		else if (!strcmp(type, "set") && !LoadStringSet(element, edit))
		{
			LU_LOG_E(LU_TAG "Load", "Failed while loading string set");
		}
	}
	edit->CommitAndInvalidate();
	return true;
}

template<typename T>
bool Impl::LoadPrimitive(const tinyxml2::XMLElement *element,
		const unique_ptr<Preferences::Editor> &edit)
{
	wstring key;
	T value{};
	bool is_has_value = false;

	for (auto attr = element->FirstAttribute(); attr; attr = attr->Next())
	{
		const char *name = attr->Name();
		if (!strcmp(name, "name"))
		{
			key = str::EncodeUtils::U8ToU16(attr->Value());
		}
		else if (!strcmp(name, "value"))
		{
			value = PrimitiveSerializer<T>::Unserialize(attr->Value());
			is_has_value = true;
		}
	}

	if (!key.empty() && is_has_value)
	{
		PutPrimitive(key, value, edit);
		return true;
	}
	else
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid key/value");
		return false;
	}
}

bool Impl::LoadString(const tinyxml2::XMLElement *element,
		const unique_ptr<Preferences::Editor> &edit)
{
	auto attr = element->FindAttribute("name");
	const char *text = element->GetText();
	if (!attr || !text)
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid node");
		return false;
	}

	wstring key = str::EncodeUtils::U8ToU16(attr->Value());
	wstring textStr = str::EncodeUtils::U8ToU16(text);
	if (!key.empty())
	{
		edit->PutString(key, std::move(textStr));
		return true;
	}
	else
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid key");
		return false;
	}
}

bool Impl::LoadStringSet(const tinyxml2::XMLElement *element,
		const unique_ptr<Preferences::Editor> &edit)
{
	auto attr = element->FindAttribute("name");
	if (!attr)
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid node");
		return false;
	}
	wstring key = str::EncodeUtils::U8ToU16(attr->Value());
	if (key.empty())
	{
		LU_LOG_E(LU_TAG "LoadString", "Invalid key");
		return false;
	}

	vector<wstring> set;
	for (auto child = element->FirstChildElement(); child;
			child = child->NextSiblingElement())
	{
		if (!strcmp(child->Name(), "string"))
		{
			const char *text = child->GetText();
			if (text)
			{
				set.push_back(str::EncodeUtils::U8ToU16(text));
			}
		}
	}
	edit->PutStringSet(key, std::move(set));
	return true;
}

}

XmlPreferencesLoader::XmlPreferencesLoader(std::istream *read)
		: m_read(read)
{}

XmlPreferencesLoader::~XmlPreferencesLoader()
{}

bool XmlPreferencesLoader::Load(const unique_ptr<Preferences::Editor> &edit)
{
	return Impl::Load(edit, m_read);
}

}
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2017 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Daniel H. Larkin
////////////////////////////////////////////////////////////////////////////////

#include "RocksDBEngine/RocksDBReplicationContext.h"
#include "Basics/StaticStrings.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringRef.h"
#include "Basics/VPackStringBufferAdapter.h"
#include "RocksDBEngine/RocksDBCollection.h"
#include "RocksDBEngine/RocksDBCommon.h"
#include "RocksDBEngine/RocksDBPrimaryIndex.h"
#include "RocksDBEngine/RocksDBTransactionState.h"
#include "Transaction/Helpers.h"
#include "Transaction/StandaloneContext.h"
#include "Transaction/UserTransaction.h"
#include "Utils/DatabaseGuard.h"
#include "VocBase/replication-common.h"
#include "VocBase/ticks.h"

#include <velocypack/Dumper.h>
#include <velocypack/velocypack-aliases.h>

using namespace arangodb;
using namespace arangodb::rocksutils;
using namespace arangodb::velocypack;

double const RocksDBReplicationContext::DefaultTTL = 30 * 60.0;

RocksDBReplicationContext::RocksDBReplicationContext()
    : _id(TRI_NewTickServer()),
      _lastTick(0),
      _currentTick(0),
      _trx(),
      _collection(nullptr),
      _iter(),
      _mdr(),
      _customTypeHandler(),
      _vpackOptions(Options::Defaults),
      _lastIteratorOffset(0),
      _expires(TRI_microtime() + DefaultTTL),
      _isDeleted(false),
      _isUsed(true),
      _hasMore(true) {}

RocksDBReplicationContext::~RocksDBReplicationContext() {
  releaseDumpingResources();
}

TRI_voc_tick_t RocksDBReplicationContext::id() const { return _id; }

uint64_t RocksDBReplicationContext::lastTick() const { return _lastTick; }

uint64_t RocksDBReplicationContext::count() const {
  TRI_ASSERT(_trx != nullptr);
  TRI_ASSERT(_collection != nullptr);
  RocksDBCollection* rcoll =
      RocksDBCollection::toRocksDBCollection(_collection->getPhysical());
  return rcoll->numberDocuments(_trx.get());
}

// creates new transaction/snapshot
void RocksDBReplicationContext::bind(TRI_vocbase_t* vocbase) {
  if ((_trx.get() == nullptr) || (_trx->vocbase() != vocbase)) {
    releaseDumpingResources();
    _trx = createTransaction(vocbase);
  }
}

int RocksDBReplicationContext::bindCollection(
    std::string const& collectionName) {
  if ((_collection == nullptr) ||
      ((_collection->name() != collectionName) &&
       std::to_string(_collection->cid()) != collectionName)) {
    _collection = _trx->vocbase()->lookupCollection(collectionName);
    if (_collection == nullptr) {
      return TRI_ERROR_BAD_PARAMETER;
    }

    _trx->addCollectionAtRuntime(collectionName);
    _iter = _collection->getAllIterator(_trx.get(), &_mdr,
                                        false);  //_mdr is not used nor updated
    _currentTick = 1;
    _hasMore = true;
  }
  return TRI_ERROR_NO_ERROR;
}

// returns inventory
std::pair<RocksDBReplicationResult, std::shared_ptr<VPackBuilder>>
RocksDBReplicationContext::getInventory(TRI_vocbase_t* vocbase,
                                        bool includeSystem) {
  TRI_ASSERT(vocbase != nullptr);
  if (_trx.get() == nullptr) {
    return std::make_pair(
        RocksDBReplicationResult(TRI_ERROR_BAD_PARAMETER, _lastTick),
        std::shared_ptr<VPackBuilder>(nullptr));
  }

  auto tick = TRI_CurrentTickServer();
  _lastTick = toRocksTransactionState(_trx.get())->sequenceNumber();
  std::shared_ptr<VPackBuilder> inventory = vocbase->inventory(
      tick, filterCollection, &includeSystem, true, sortCollections);

  return std::make_pair(RocksDBReplicationResult(TRI_ERROR_NO_ERROR, _lastTick),
                        inventory);
}

// iterates over at most 'limit' documents in the collection specified,
// creating a new iterator if one does not exist for this collection
RocksDBReplicationResult RocksDBReplicationContext::dump(
    TRI_vocbase_t* vocbase, std::string const& collectionName,
    basics::StringBuffer& buff, uint64_t chunkSize) {
  TRI_ASSERT(vocbase != nullptr);
  if (_trx.get() == nullptr) {
    return RocksDBReplicationResult(TRI_ERROR_BAD_PARAMETER, _lastTick);
  }
  int res = bindCollection(collectionName);
  if (res != TRI_ERROR_NO_ERROR) {
    return RocksDBReplicationResult(res, _lastTick);
  }

  // set type
  int type = 2300;  // documents
  if (_collection->type() == TRI_COL_TYPE_EDGE) {
    type = 2301;  // edge documents
  }

  arangodb::basics::VPackStringBufferAdapter adapter(buff.stringBuffer());

  VPackBuilder builder(&_vpackOptions);

  auto cb = [this, &type, &buff, &adapter,
             &builder](DocumentIdentifierToken const& token) {
    builder.clear();

    builder.openObject();
    // set type
    builder.add("type", VPackValue(type));

    // set data
    bool ok = _collection->readDocument(_trx.get(), token, _mdr);

    if (!ok) {
      LOG_TOPIC(ERR, Logger::REPLICATION)
          << "could not get document with token: " << token._data;
      throw RocksDBReplicationResult(TRI_ERROR_INTERNAL, _lastTick);
    }

    builder.add(VPackValue("data"));
    _mdr.addToBuilder(builder, false);
    builder.add("key", builder.slice().get(StaticStrings::KeyString));
    builder.close();

    VPackDumper dumper(
        &adapter,
        &_vpackOptions);  // note: we need the CustomTypeHandler here
    VPackSlice slice = builder.slice();
    dumper.dump(slice);
    buff.appendChar('\n');
  };

  while (_hasMore && buff.length() < chunkSize) {
    try {
      _hasMore = _iter->next(cb, 10);  // TODO: adjust limit?
    } catch (std::exception const& ex) {
      _hasMore = false;
      return RocksDBReplicationResult(TRI_ERROR_INTERNAL, _lastTick);
    } catch (RocksDBReplicationResult const& ex) {
      _hasMore = false;
      return ex;
    }
  }

  if (_hasMore) {
    _currentTick++;
  }

  return RocksDBReplicationResult(TRI_ERROR_NO_ERROR, _currentTick);
}

arangodb::Result RocksDBReplicationContext::dumpKeyChunks(VPackBuilder& b,
                                                          uint64_t chunkSize) {
  TRI_ASSERT(_trx);
  TRI_ASSERT(_iter);

  RocksDBAllIndexIterator* primary =
      static_cast<RocksDBAllIndexIterator*>(_iter.get());

  std::string lowKey;
  VPackSlice highKey;  // FIXME: no good keeping this

  uint64_t hash = 0x012345678;
  auto cb = [&](DocumentIdentifierToken const& token) {
    bool ok = _collection->readDocument(_trx.get(), token, _mdr);
    if (!ok) {
      // TODO: do something here?
      return;
    }

    // current document
    VPackSlice current(_mdr.vpack());
    highKey = current.get(StaticStrings::KeyString);
    // set type
    if (lowKey.empty()) {
      lowKey = highKey.copyString();
    }

    // we can get away with the fast hash function here, as key values are
    // restricted to strings
    hash ^= transaction::helpers::extractKeyFromDocument(current).hashString();
    hash ^= transaction::helpers::extractRevSliceFromDocument(current).hash();
  };

  b.openArray();
  while (_hasMore) {
    try {
      _hasMore = primary->next(cb, chunkSize);

      b.add(VPackValue(VPackValueType::Object));
      b.add("low", VPackValue(lowKey));
      b.add("high", VPackValue(highKey.copyString()));
      b.add("hash", VPackValue(std::to_string(hash)));
      b.close();
      lowKey = "";
    } catch (std::exception const& ex) {
      return Result(TRI_ERROR_INTERNAL);
    }
  }
  b.close();

  return Result();
}

/// dump all keys from collection
arangodb::Result RocksDBReplicationContext::dumpKeys(VPackBuilder& b,
                                                     size_t chunk,
                                                     size_t chunkSize) {
  TRI_ASSERT(_trx);
  TRI_ASSERT(_iter);

  // Position the iterator correctly
  size_t from = chunk * chunkSize;
  if (from == 0 || !_hasMore || from < _lastIteratorOffset) {
    _iter->reset();
    _hasMore = true;
    _lastIteratorOffset = 0;
  }
  if (from > _lastIteratorOffset) {
    TRI_ASSERT(from >= chunkSize);
    uint64_t diff = from - _lastIteratorOffset;
    uint64_t to = 0;  // = (chunk + 1) * chunkSize;
    _iter->skip(diff, to);
    _lastIteratorOffset += to;
    TRI_ASSERT(to == diff);
  } else if (from < _lastIteratorOffset) {
    // no jumping back in time fix the intitial syncer if you see this
    LOG_TOPIC(ERR, Logger::REPLICATION)
        << "Trying to request a chunk the rocksdb "
        << "iterator already passed over";
    return Result(TRI_ERROR_INTERNAL);
  }

  RocksDBAllIndexIterator* primary =
      static_cast<RocksDBAllIndexIterator*>(_iter.get());
  auto cb = [&](DocumentIdentifierToken const& token, StringRef const& key) {
    RocksDBToken const& rt = static_cast<RocksDBToken const&>(token);

    b.openArray();
    b.add(VPackValuePair(key.data(), key.size(), VPackValueType::String));
    b.add(VPackValue(std::to_string(rt.revisionId())));
    b.close();
  };

  b.openArray();
  // chunkSize is going to be ignored here
  if (_hasMore) {
    try {
      _hasMore = primary->nextWithKey(cb, chunkSize);
      _lastIteratorOffset++;
    } catch (std::exception const& ex) {
      return Result(TRI_ERROR_INTERNAL);
    }
  }
  b.close();

  return Result();
}

/// dump keys and document
arangodb::Result RocksDBReplicationContext::dumpDocuments(
    VPackBuilder& b, size_t chunk, size_t chunkSize, VPackSlice const& ids) {
  TRI_ASSERT(_trx);
  TRI_ASSERT(_iter);

  // Position the iterator must be reset to the beginning
  // after calls to dumpKeys moved it forwards
  size_t from = chunk * chunkSize;
  if (from == 0 || !_hasMore || from < _lastIteratorOffset) {
    _iter->reset();
    _hasMore = true;
    _lastIteratorOffset = 0;
  }
  if (from > _lastIteratorOffset) {
    TRI_ASSERT(from >= chunkSize);
    uint64_t diff = from - _lastIteratorOffset;
    uint64_t to = 0;  // = (chunk + 1) * chunkSize;
    _iter->skip(diff, to);
    _lastIteratorOffset += to;
    TRI_ASSERT(to == diff);
  }

  auto cb = [&](DocumentIdentifierToken const& token) {
    bool ok = _collection->readDocument(_trx.get(), token, _mdr);
    if (!ok) {
      // TODO: do something here?
      return;
    }
    VPackSlice current(_mdr.vpack());
    TRI_ASSERT(current.isObject());
    b.add(current);
  };

  b.openArray();
  bool hasMore = true;
  size_t oldPos = from;
  for (auto const& it : VPackArrayIterator(ids)) {
    if (!it.isNumber()) {
      return Result(TRI_ERROR_BAD_PARAMETER);
    }
    if (!hasMore) {
      LOG_TOPIC(ERR, Logger::REPLICATION) << "Not enough data";
      return Result(TRI_ERROR_FAILED);
    }

    size_t newPos = from + it.getNumber<size_t>();
    if (oldPos != from && newPos > oldPos + 1) {
      uint64_t ignore = 0;
      _iter->skip(newPos - oldPos, ignore);
      TRI_ASSERT(ignore == newPos - oldPos);
      _lastIteratorOffset += ignore;
    }
    hasMore = _iter->next(cb, 1);
    _lastIteratorOffset++;
  }
  b.close();

  return Result();
}

double RocksDBReplicationContext::expires() const { return _expires; }

bool RocksDBReplicationContext::isDeleted() const { return _isDeleted; }

void RocksDBReplicationContext::deleted() { _isDeleted = true; }

bool RocksDBReplicationContext::isUsed() const { return _isUsed; }
bool RocksDBReplicationContext::more() const { return _hasMore; }

void RocksDBReplicationContext::use(double ttl) {
  TRI_ASSERT(!_isDeleted);
  TRI_ASSERT(!_isUsed);

  _isUsed = true;
  _expires = TRI_microtime() + ttl;
}

void RocksDBReplicationContext::release() {
  TRI_ASSERT(_isUsed);
  _isUsed = false;
}

void RocksDBReplicationContext::releaseDumpingResources() {
  if (_trx.get() != nullptr) {
    _trx->abort();
    _trx.reset();
  }
  if (_iter.get() != nullptr) {
    _iter.reset();
  }
  _collection = nullptr;
  _guard.reset();
}

std::unique_ptr<transaction::Methods>
RocksDBReplicationContext::createTransaction(TRI_vocbase_t* vocbase) {
  _guard.reset(new DatabaseGuard(vocbase));

  double lockTimeout = transaction::Methods::DefaultLockTimeout;
  std::shared_ptr<transaction::StandaloneContext> ctx =
      transaction::StandaloneContext::Create(vocbase);
  std::unique_ptr<transaction::Methods> trx(new transaction::UserTransaction(
      ctx, {}, {}, {}, lockTimeout, false, true));
  Result res = trx->begin();
  if (!res.ok()) {
    _guard.reset();
    THROW_ARANGO_EXCEPTION(res);
  }
  _customTypeHandler = ctx->orderCustomTypeHandler();
  _vpackOptions.customTypeHandler = _customTypeHandler.get();

  return trx;
}

/// @brief filter a collection based on collection attributes
bool RocksDBReplicationContext::filterCollection(
    arangodb::LogicalCollection* collection, void* data) {
  bool includeSystem = *((bool*)data);

  std::string const collectionName(collection->name());

  if (!includeSystem && collectionName[0] == '_') {
    // exclude all system collections
    return false;
  }

  if (TRI_ExcludeCollectionReplication(collectionName.c_str(), includeSystem)) {
    // collection is excluded from replication
    return false;
  }

  // all other cases should be included
  return true;
}

bool RocksDBReplicationContext::sortCollections(
    arangodb::LogicalCollection const* l,
    arangodb::LogicalCollection const* r) {
  if (l->type() != r->type()) {
    return l->type() < r->type();
  }
  std::string const leftName = l->name();
  std::string const rightName = r->name();

  return strcasecmp(leftName.c_str(), rightName.c_str()) < 0;
}
<commit_msg>Fixed Dump. It tried to insert contains of a builder into itself<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2017 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Daniel H. Larkin
////////////////////////////////////////////////////////////////////////////////

#include "RocksDBEngine/RocksDBReplicationContext.h"
#include "Basics/StaticStrings.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringRef.h"
#include "Basics/VPackStringBufferAdapter.h"
#include "VocBase/replication-common.h"
#include "RocksDBEngine/RocksDBCollection.h"
#include "RocksDBEngine/RocksDBCommon.h"
#include "RocksDBEngine/RocksDBPrimaryIndex.h"
#include "RocksDBEngine/RocksDBTransactionState.h"
#include "Transaction/Helpers.h"
#include "Transaction/StandaloneContext.h"
#include "Transaction/UserTransaction.h"
#include "Utils/DatabaseGuard.h"
#include "VocBase/replication-common.h"
#include "VocBase/ticks.h"

#include <velocypack/Dumper.h>
#include <velocypack/velocypack-aliases.h>

using namespace arangodb;
using namespace arangodb::rocksutils;
using namespace arangodb::velocypack;

double const RocksDBReplicationContext::DefaultTTL = 30 * 60.0;

RocksDBReplicationContext::RocksDBReplicationContext()
    : _id(TRI_NewTickServer()),
      _lastTick(0),
      _currentTick(0),
      _trx(),
      _collection(nullptr),
      _iter(),
      _mdr(),
      _customTypeHandler(),
      _vpackOptions(Options::Defaults),
      _lastIteratorOffset(0),
      _expires(TRI_microtime() + DefaultTTL),
      _isDeleted(false),
      _isUsed(true),
      _hasMore(true) {}

RocksDBReplicationContext::~RocksDBReplicationContext() {
  releaseDumpingResources();
}

TRI_voc_tick_t RocksDBReplicationContext::id() const { return _id; }

uint64_t RocksDBReplicationContext::lastTick() const { return _lastTick; }

uint64_t RocksDBReplicationContext::count() const {
  TRI_ASSERT(_trx != nullptr);
  TRI_ASSERT(_collection != nullptr);
  RocksDBCollection* rcoll =
      RocksDBCollection::toRocksDBCollection(_collection->getPhysical());
  return rcoll->numberDocuments(_trx.get());
}

// creates new transaction/snapshot
void RocksDBReplicationContext::bind(TRI_vocbase_t* vocbase) {
  if ((_trx.get() == nullptr) || (_trx->vocbase() != vocbase)) {
    releaseDumpingResources();
    _trx = createTransaction(vocbase);
  }
}

int RocksDBReplicationContext::bindCollection(
    std::string const& collectionName) {
  if ((_collection == nullptr) ||
      ((_collection->name() != collectionName) &&
       std::to_string(_collection->cid()) != collectionName)) {
    _collection = _trx->vocbase()->lookupCollection(collectionName);
    if (_collection == nullptr) {
      return TRI_ERROR_BAD_PARAMETER;
    }

    _trx->addCollectionAtRuntime(collectionName);
    _iter = _collection->getAllIterator(_trx.get(), &_mdr,
                                        false);  //_mdr is not used nor updated
    _currentTick = 1;
    _hasMore = true;
  }
  return TRI_ERROR_NO_ERROR;
}

// returns inventory
std::pair<RocksDBReplicationResult, std::shared_ptr<VPackBuilder>>
RocksDBReplicationContext::getInventory(TRI_vocbase_t* vocbase,
                                        bool includeSystem) {
  TRI_ASSERT(vocbase != nullptr);
  if (_trx.get() == nullptr) {
    return std::make_pair(
        RocksDBReplicationResult(TRI_ERROR_BAD_PARAMETER, _lastTick),
        std::shared_ptr<VPackBuilder>(nullptr));
  }

  auto tick = TRI_CurrentTickServer();
  _lastTick = toRocksTransactionState(_trx.get())->sequenceNumber();
  std::shared_ptr<VPackBuilder> inventory = vocbase->inventory(
      tick, filterCollection, &includeSystem, true, sortCollections);

  return std::make_pair(RocksDBReplicationResult(TRI_ERROR_NO_ERROR, _lastTick),
                        inventory);
}

// iterates over at most 'limit' documents in the collection specified,
// creating a new iterator if one does not exist for this collection
RocksDBReplicationResult RocksDBReplicationContext::dump(
    TRI_vocbase_t* vocbase, std::string const& collectionName,
    basics::StringBuffer& buff, uint64_t chunkSize) {
  TRI_ASSERT(vocbase != nullptr);
  if (_trx.get() == nullptr) {
    return RocksDBReplicationResult(TRI_ERROR_BAD_PARAMETER, _lastTick);
  }
  int res = bindCollection(collectionName);
  if (res != TRI_ERROR_NO_ERROR) {
    return RocksDBReplicationResult(res, _lastTick);
  }

  // set type
  int type = REPLICATION_MARKER_DOCUMENT;  // documents

  arangodb::basics::VPackStringBufferAdapter adapter(buff.stringBuffer());

  VPackBuilder builder(&_vpackOptions);

  auto cb = [this, &type, &buff, &adapter,
             &builder](DocumentIdentifierToken const& token) {
    builder.clear();

    builder.openObject();
    // set type
    builder.add("type", VPackValue(type));

    // set data
    bool ok = _collection->readDocument(_trx.get(), token, _mdr);

    if (!ok) {
      LOG_TOPIC(ERR, Logger::REPLICATION)
          << "could not get document with token: " << token._data;
      throw RocksDBReplicationResult(TRI_ERROR_INTERNAL, _lastTick);
    }

    builder.add(VPackValue("data"));
    auto key = VPackSlice(_mdr.vpack()).get(StaticStrings::KeyString);
    _mdr.addToBuilder(builder, false);
    builder.add("key", key);
    builder.close();

    VPackDumper dumper(
        &adapter,
        &_vpackOptions);  // note: we need the CustomTypeHandler here
    VPackSlice slice = builder.slice();
    dumper.dump(slice);
    buff.appendChar('\n');
  };

  while (_hasMore && buff.length() < chunkSize) {
    try {
      _hasMore = _iter->next(cb, 10);  // TODO: adjust limit?
    } catch (std::exception const& ex) {
      _hasMore = false;
      return RocksDBReplicationResult(TRI_ERROR_INTERNAL, _lastTick);
    } catch (RocksDBReplicationResult const& ex) {
      _hasMore = false;
      return ex;
    }
  }

  if (_hasMore) {
    _currentTick++;
  }

  return RocksDBReplicationResult(TRI_ERROR_NO_ERROR, _currentTick);
}

arangodb::Result RocksDBReplicationContext::dumpKeyChunks(VPackBuilder& b,
                                                          uint64_t chunkSize) {
  TRI_ASSERT(_trx);
  TRI_ASSERT(_iter);

  RocksDBAllIndexIterator* primary =
      static_cast<RocksDBAllIndexIterator*>(_iter.get());

  std::string lowKey;
  VPackSlice highKey;  // FIXME: no good keeping this

  uint64_t hash = 0x012345678;
  auto cb = [&](DocumentIdentifierToken const& token) {
    bool ok = _collection->readDocument(_trx.get(), token, _mdr);
    if (!ok) {
      // TODO: do something here?
      return;
    }

    // current document
    VPackSlice current(_mdr.vpack());
    highKey = current.get(StaticStrings::KeyString);
    // set type
    if (lowKey.empty()) {
      lowKey = highKey.copyString();
    }

    // we can get away with the fast hash function here, as key values are
    // restricted to strings
    hash ^= transaction::helpers::extractKeyFromDocument(current).hashString();
    hash ^= transaction::helpers::extractRevSliceFromDocument(current).hash();
  };

  b.openArray();
  while (_hasMore) {
    try {
      _hasMore = primary->next(cb, chunkSize);

      b.add(VPackValue(VPackValueType::Object));
      b.add("low", VPackValue(lowKey));
      b.add("high", VPackValue(highKey.copyString()));
      b.add("hash", VPackValue(std::to_string(hash)));
      b.close();
      lowKey = "";
    } catch (std::exception const& ex) {
      return Result(TRI_ERROR_INTERNAL);
    }
  }
  b.close();

  return Result();
}

/// dump all keys from collection
arangodb::Result RocksDBReplicationContext::dumpKeys(VPackBuilder& b,
                                                     size_t chunk,
                                                     size_t chunkSize) {
  TRI_ASSERT(_trx);
  TRI_ASSERT(_iter);

  // Position the iterator correctly
  size_t from = chunk * chunkSize;
  if (from == 0 || !_hasMore || from < _lastIteratorOffset) {
    _iter->reset();
    _hasMore = true;
    _lastIteratorOffset = 0;
  }
  if (from > _lastIteratorOffset) {
    TRI_ASSERT(from >= chunkSize);
    uint64_t diff = from - _lastIteratorOffset;
    uint64_t to = 0;  // = (chunk + 1) * chunkSize;
    _iter->skip(diff, to);
    _lastIteratorOffset += to;
    TRI_ASSERT(to == diff);
  } else if (from < _lastIteratorOffset) {
    // no jumping back in time fix the intitial syncer if you see this
    LOG_TOPIC(ERR, Logger::REPLICATION)
        << "Trying to request a chunk the rocksdb "
        << "iterator already passed over";
    return Result(TRI_ERROR_INTERNAL);
  }

  RocksDBAllIndexIterator* primary =
      static_cast<RocksDBAllIndexIterator*>(_iter.get());
  auto cb = [&](DocumentIdentifierToken const& token, StringRef const& key) {
    RocksDBToken const& rt = static_cast<RocksDBToken const&>(token);

    b.openArray();
    b.add(VPackValuePair(key.data(), key.size(), VPackValueType::String));
    b.add(VPackValue(std::to_string(rt.revisionId())));
    b.close();
  };

  b.openArray();
  // chunkSize is going to be ignored here
  if (_hasMore) {
    try {
      _hasMore = primary->nextWithKey(cb, chunkSize);
      _lastIteratorOffset++;
    } catch (std::exception const& ex) {
      return Result(TRI_ERROR_INTERNAL);
    }
  }
  b.close();

  return Result();
}

/// dump keys and document
arangodb::Result RocksDBReplicationContext::dumpDocuments(
    VPackBuilder& b, size_t chunk, size_t chunkSize, VPackSlice const& ids) {
  TRI_ASSERT(_trx);
  TRI_ASSERT(_iter);

  // Position the iterator must be reset to the beginning
  // after calls to dumpKeys moved it forwards
  size_t from = chunk * chunkSize;
  if (from == 0 || !_hasMore || from < _lastIteratorOffset) {
    _iter->reset();
    _hasMore = true;
    _lastIteratorOffset = 0;
  }
  if (from > _lastIteratorOffset) {
    TRI_ASSERT(from >= chunkSize);
    uint64_t diff = from - _lastIteratorOffset;
    uint64_t to = 0;  // = (chunk + 1) * chunkSize;
    _iter->skip(diff, to);
    _lastIteratorOffset += to;
    TRI_ASSERT(to == diff);
  }

  auto cb = [&](DocumentIdentifierToken const& token) {
    bool ok = _collection->readDocument(_trx.get(), token, _mdr);
    if (!ok) {
      // TODO: do something here?
      return;
    }
    VPackSlice current(_mdr.vpack());
    TRI_ASSERT(current.isObject());
    b.add(current);
  };

  b.openArray();
  bool hasMore = true;
  size_t oldPos = from;
  for (auto const& it : VPackArrayIterator(ids)) {
    if (!it.isNumber()) {
      return Result(TRI_ERROR_BAD_PARAMETER);
    }
    if (!hasMore) {
      LOG_TOPIC(ERR, Logger::REPLICATION) << "Not enough data";
      return Result(TRI_ERROR_FAILED);
    }

    size_t newPos = from + it.getNumber<size_t>();
    if (oldPos != from && newPos > oldPos + 1) {
      uint64_t ignore = 0;
      _iter->skip(newPos - oldPos, ignore);
      TRI_ASSERT(ignore == newPos - oldPos);
      _lastIteratorOffset += ignore;
    }
    hasMore = _iter->next(cb, 1);
    _lastIteratorOffset++;
  }
  b.close();

  return Result();
}

double RocksDBReplicationContext::expires() const { return _expires; }

bool RocksDBReplicationContext::isDeleted() const { return _isDeleted; }

void RocksDBReplicationContext::deleted() { _isDeleted = true; }

bool RocksDBReplicationContext::isUsed() const { return _isUsed; }
bool RocksDBReplicationContext::more() const { return _hasMore; }

void RocksDBReplicationContext::use(double ttl) {
  TRI_ASSERT(!_isDeleted);
  TRI_ASSERT(!_isUsed);

  _isUsed = true;
  _expires = TRI_microtime() + ttl;
}

void RocksDBReplicationContext::release() {
  TRI_ASSERT(_isUsed);
  _isUsed = false;
}

void RocksDBReplicationContext::releaseDumpingResources() {
  if (_trx.get() != nullptr) {
    _trx->abort();
    _trx.reset();
  }
  if (_iter.get() != nullptr) {
    _iter.reset();
  }
  _collection = nullptr;
  _guard.reset();
}

std::unique_ptr<transaction::Methods>
RocksDBReplicationContext::createTransaction(TRI_vocbase_t* vocbase) {
  _guard.reset(new DatabaseGuard(vocbase));

  double lockTimeout = transaction::Methods::DefaultLockTimeout;
  std::shared_ptr<transaction::StandaloneContext> ctx =
      transaction::StandaloneContext::Create(vocbase);
  std::unique_ptr<transaction::Methods> trx(new transaction::UserTransaction(
      ctx, {}, {}, {}, lockTimeout, false, true));
  Result res = trx->begin();
  if (!res.ok()) {
    _guard.reset();
    THROW_ARANGO_EXCEPTION(res);
  }
  _customTypeHandler = ctx->orderCustomTypeHandler();
  _vpackOptions.customTypeHandler = _customTypeHandler.get();

  return trx;
}

/// @brief filter a collection based on collection attributes
bool RocksDBReplicationContext::filterCollection(
    arangodb::LogicalCollection* collection, void* data) {
  bool includeSystem = *((bool*)data);

  std::string const collectionName(collection->name());

  if (!includeSystem && collectionName[0] == '_') {
    // exclude all system collections
    return false;
  }

  if (TRI_ExcludeCollectionReplication(collectionName.c_str(), includeSystem)) {
    // collection is excluded from replication
    return false;
  }

  // all other cases should be included
  return true;
}

bool RocksDBReplicationContext::sortCollections(
    arangodb::LogicalCollection const* l,
    arangodb::LogicalCollection const* r) {
  if (l->type() != r->type()) {
    return l->type() < r->type();
  }
  std::string const leftName = l->name();
  std::string const rightName = r->name();

  return strcasecmp(leftName.c_str(), rightName.c_str()) < 0;
}
<|endoftext|>