<commit_before>/******************************************************************************\
* File:          lexer.cpp
* Purpose:       Implementation of lexer classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/stc/stc.h> // for wxSTC_KEYWORDSET_MAX
#include <wx/tokenzr.h>
#include <wx/extension/lexer.h>
#include <wx/extension/util.h> // for exGetWord

using namespace std;

const wxString exLexer::GetKeywordsString(int keyword_set) const
{
  wxString keywords;

  if (keyword_set == -1)
  {
    for (
      set<wxString>::const_iterator it = m_Keywords.begin();
      it != m_Keywords.end();
      ++it)
    {
      keywords += *it + " ";
    }
  }
  else
  {
    std::map< int, std::set<wxString> >::const_iterator it = m_KeywordsSet.find(keyword_set);

    if (it == m_KeywordsSet.end())
    {
      wxFAIL;
    }
    else
    {
      set<wxString> theset = it->second;

      for (
        set<wxString>::const_iterator it = theset.begin();
        it != theset.end();
        ++it)
      {
        keywords += *it + " ";
      }
    }
  }

  return keywords;
}

const wxString exLexer::GetFormattedText(
  const wxString& lines,
  const wxString& header,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString text = lines, header_to_use = header;
  size_t nCharIndex;

  wxString out;

  // Process text between the carriage return line feeds.
  while ((nCharIndex = text.find("\n")) != wxString::npos)
  {
    out << GetUnFormattedText(
      text.substr(0, nCharIndex),
      header_to_use,
      fill_out_with_space,
      fill_out);

    text = text.substr(nCharIndex + 1);
    header_to_use = wxString(' ', header.size());
  }

  if (!text.empty())
  {
    out << GetUnFormattedText(
      text,
      header_to_use,
      fill_out_with_space,
      fill_out);
  }

  return out;
}

const wxString exLexer::GetUnFormattedText(
  const wxString& lines,
  const wxString& header,
  bool fill_out_with_space,
  bool fill_out) const
{
  const size_t line_length = UsableCharactersPerLine();

  // Use the header, with one space extra to separate, or no header at all.
  const wxString header_with_spaces =
    (header.size() == 0) ? wxString(wxEmptyString) : wxString(' ', header.size());

  wxString in = lines, line = header;

  bool at_begin = true;

  wxString out;

  while (!in.empty())
  {
    const wxString word = exGetWord(in, false, false);

    if (line.size() + 1 + word.size() > line_length)
    {
      out << MakeSingleLineComment(line, fill_out_with_space, fill_out) << "\n";

      line = header_with_spaces + word;
      at_begin = true;
    }
    else
    {
      line += (!line.empty() && !at_begin ? " ": wxString(wxEmptyString)) + word;
      at_begin = false;
    }
  }

  out << MakeSingleLineComment(line, fill_out_with_space, fill_out);

  return out;
}

bool exLexer::IsKeyword(const wxString& word) const
{
  set<wxString>::const_iterator it = m_Keywords.find(word);
  return (it != m_Keywords.end());
}

bool exLexer::KeywordStartsWith(const wxString& word) const
{
  for (
    set<wxString>::const_iterator it = m_Keywords.begin();
    it != m_Keywords.end();
    ++it)
  {
    if (it->Upper().StartsWith(word.Upper()))
    {
      return true;
    }
  }

  return false;
}

const wxString exLexer::MakeComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, wxEmptyString, fill_out_with_space, fill_out):
    out << GetUnFormattedText(text, wxEmptyString, fill_out_with_space, fill_out);

  return out;
}

const wxString exLexer::MakeComment(
  const wxString& prefix,
  const wxString& text) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, prefix, true, true):
    out << GetUnFormattedText(text, prefix, true, true);

  return out;
}

const wxString exLexer::MakeSingleLineComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  // First set the fill_out_character.
  wxChar fill_out_character;

  if (fill_out_with_space)
  {
    fill_out_character = ' ';
  }
  else
  {
    if (text.empty())
    {
      if (m_CommentBegin == m_CommentEnd || m_CommentEnd.empty())
           fill_out_character = '-';
      else fill_out_character = m_CommentBegin[m_CommentBegin.size() - 1];
    }
    else   fill_out_character = ' ';
  }

  wxString out = m_CommentBegin + fill_out_character + text;

  // Fill out characters.
  if (fill_out)
  {
    const int fill_chars = UsableCharactersPerLine() - text.size();

    if (fill_chars > 0)
    {
      const wxString fill_out(fill_out_character, fill_chars);
      out += fill_out;
    }
  }

  if (!m_CommentEnd.empty()) out += fill_out_character + m_CommentEnd;

  return out;
}

bool exLexer::SetKeywords(const wxString& value)
{
  set<wxString> keywords_set;

  wxStringTokenizer tkz(value, "\r\n ");

  int setno = 0;

  while (tkz.HasMoreTokens())
  {
    const wxString line = tkz.GetNextToken();
    wxStringTokenizer fields(line, ":");

    wxString keyword;

    if (fields.CountTokens() > 1)
    {
      keyword = fields.GetNextToken();

      const int new_setno = atoi(fields.GetNextToken().c_str());

      if (new_setno >= wxSTC_KEYWORDSET_MAX)
      {
        return false;
      }

      if (new_setno != setno)
      {
        if (!keywords_set.empty())
        {
          m_KeywordsSet.insert(make_pair(setno, keywords_set));
          keywords_set.clear();
        }

        setno = new_setno;
      }

      keywords_set.insert(keyword);
    }
    else
    {
      keyword = line;
      keywords_set.insert(line);
    }

    m_Keywords.insert(keyword);
  }

  m_KeywordsSet.insert(make_pair(setno, keywords_set));

  return true;
}

int exLexer::UsableCharactersPerLine() const
{
  // We always use lines with 80 characters. We adjust this here for
  // the space the beginning and end of the comment characters occupy.
  return 80
      -  (m_CommentBegin.size() + 1)
      - ((m_CommentEnd.size() != 0) ? m_CommentEnd.size() + 1 : 0);
}
<commit_msg>fixed spacing error<commit_after>/******************************************************************************\
* File:          lexer.cpp
* Purpose:       Implementation of lexer classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/stc/stc.h> // for wxSTC_KEYWORDSET_MAX
#include <wx/tokenzr.h>
#include <wx/extension/lexer.h>
#include <wx/extension/util.h> // for exGetWord

using namespace std;

const wxString exLexer::GetKeywordsString(int keyword_set) const
{
  wxString keywords;

  if (keyword_set == -1)
  {
    for (
      set<wxString>::const_iterator it = m_Keywords.begin();
      it != m_Keywords.end();
      ++it)
    {
      keywords += *it + " ";
    }
  }
  else
  {
    std::map< int, std::set<wxString> >::const_iterator it = m_KeywordsSet.find(keyword_set);

    if (it == m_KeywordsSet.end())
    {
      wxFAIL;
    }
    else
    {
      set<wxString> theset = it->second;

      for (
        set<wxString>::const_iterator it = theset.begin();
        it != theset.end();
        ++it)
      {
        keywords += *it + " ";
      }
    }
  }

  return keywords;
}

const wxString exLexer::GetFormattedText(
  const wxString& lines,
  const wxString& header,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString text = lines, header_to_use = header;
  size_t nCharIndex;

  wxString out;

  // Process text between the carriage return line feeds.
  while ((nCharIndex = text.find("\n")) != wxString::npos)
  {
    out << GetUnFormattedText(
      text.substr(0, nCharIndex),
      header_to_use,
      fill_out_with_space,
      fill_out);

    text = text.substr(nCharIndex + 1);
    header_to_use = wxString(' ', header.size());
  }

  if (!text.empty())
  {
    out << GetUnFormattedText(
      text,
      header_to_use,
      fill_out_with_space,
      fill_out);
  }

  return out;
}

const wxString exLexer::GetUnFormattedText(
  const wxString& lines,
  const wxString& header,
  bool fill_out_with_space,
  bool fill_out) const
{
  const size_t line_length = UsableCharactersPerLine();

  // Use the header, with one space extra to separate, or no header at all.
  const wxString header_with_spaces =
    (header.size() == 0) ? wxString(wxEmptyString) : wxString(' ', header.size());

  wxString in = lines, line = header;

  bool at_begin = true;
  wxString out;

  while (!in.empty())
  {
    const wxString word = exGetWord(in, false, false);

    if (line.size() + 1 + word.size() > line_length)
    {
      out << MakeSingleLineComment(line, fill_out_with_space, fill_out) << "\n";

      line = header_with_spaces + word;
    }
    else
    {
      line += (!line.empty() && !at_begin ? " ": wxString(wxEmptyString)) + word;
      at_begin = false;
    }
  }

  out << MakeSingleLineComment(line, fill_out_with_space, fill_out);

  return out;
}

bool exLexer::IsKeyword(const wxString& word) const
{
  set<wxString>::const_iterator it = m_Keywords.find(word);
  return (it != m_Keywords.end());
}

bool exLexer::KeywordStartsWith(const wxString& word) const
{
  for (
    set<wxString>::const_iterator it = m_Keywords.begin();
    it != m_Keywords.end();
    ++it)
  {
    if (it->Upper().StartsWith(word.Upper()))
    {
      return true;
    }
  }

  return false;
}

const wxString exLexer::MakeComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, wxEmptyString, fill_out_with_space, fill_out):
    out << GetUnFormattedText(text, wxEmptyString, fill_out_with_space, fill_out);

  return out;
}

const wxString exLexer::MakeComment(
  const wxString& prefix,
  const wxString& text) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, prefix, true, true):
    out << GetUnFormattedText(text, prefix, true, true);

  return out;
}

const wxString exLexer::MakeSingleLineComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  // First set the fill_out_character.
  wxChar fill_out_character;

  if (fill_out_with_space)
  {
    fill_out_character = ' ';
  }
  else
  {
    if (text.empty())
    {
      if (m_CommentBegin == m_CommentEnd || m_CommentEnd.empty())
           fill_out_character = '-';
      else fill_out_character = m_CommentBegin[m_CommentBegin.size() - 1];
    }
    else   fill_out_character = ' ';
  }

  wxString out = m_CommentBegin + fill_out_character + text;

  // Fill out characters.
  if (fill_out)
  {
    const int fill_chars = UsableCharactersPerLine() - text.size();

    if (fill_chars > 0)
    {
      const wxString fill_out(fill_out_character, fill_chars);
      out += fill_out;
    }
  }

  if (!m_CommentEnd.empty()) out += fill_out_character + m_CommentEnd;

  return out;
}

bool exLexer::SetKeywords(const wxString& value)
{
  set<wxString> keywords_set;

  wxStringTokenizer tkz(value, "\r\n ");

  int setno = 0;

  while (tkz.HasMoreTokens())
  {
    const wxString line = tkz.GetNextToken();
    wxStringTokenizer fields(line, ":");

    wxString keyword;

    if (fields.CountTokens() > 1)
    {
      keyword = fields.GetNextToken();

      const int new_setno = atoi(fields.GetNextToken().c_str());

      if (new_setno >= wxSTC_KEYWORDSET_MAX)
      {
        return false;
      }

      if (new_setno != setno)
      {
        if (!keywords_set.empty())
        {
          m_KeywordsSet.insert(make_pair(setno, keywords_set));
          keywords_set.clear();
        }

        setno = new_setno;
      }

      keywords_set.insert(keyword);
    }
    else
    {
      keyword = line;
      keywords_set.insert(line);
    }

    m_Keywords.insert(keyword);
  }

  m_KeywordsSet.insert(make_pair(setno, keywords_set));

  return true;
}

int exLexer::UsableCharactersPerLine() const
{
  // We always use lines with 80 characters. We adjust this here for
  // the space the beginning and end of the comment characters occupy.
  return 80
      -  (m_CommentBegin.size() + 1)
      - ((m_CommentEnd.size() != 0) ? m_CommentEnd.size() + 1 : 0);
}
<|endoftext|>


<commit_before>#ifndef hash_map_hh_INCLUDED
#define hash_map_hh_INCLUDED

#include "hash.hh"
#include "memory.hh"
#include "vector.hh"

namespace Kakoune
{

template<typename T>
constexpr void constexpr_swap(T& lhs, T& rhs)
{
    T tmp = std::move(lhs);
    lhs = std::move(rhs);
    rhs = std::move(tmp);
}

template<MemoryDomain domain,
         template<typename, MemoryDomain> class Container>
struct HashIndex
{
    struct Entry
    {
        size_t hash = 0;
        int index = -1;
    };

    static constexpr float max_fill_rate = 0.5f;

    constexpr HashIndex() = default;
    constexpr HashIndex(size_t count)
    {
        const size_t min_size = (size_t)(count / max_fill_rate) + 1;
        size_t new_size = 4;
        while (new_size < min_size)
            new_size *= 2;
        m_entries.resize(new_size, {});
    }

    using ContainerType = Container<Entry, domain>;

    constexpr void resize(size_t new_size)
    {
        kak_assert(new_size > m_entries.size());
        ContainerType old_entries = std::move(m_entries);
        m_entries.resize(new_size, {});
        for (auto& entry : old_entries)
        {
            if (entry.index >= 0)
                add(entry.hash, entry.index);
        }
    }

    constexpr void reserve(size_t count)
    {
        kak_assert(count > 0);
        const size_t min_size = (size_t)(count / max_fill_rate) + 1;
        size_t new_size = m_entries.empty() ? 4 : m_entries.size();
        while (new_size < min_size)
            new_size *= 2;

        if (new_size > m_entries.size())
            resize(new_size);
    }

    constexpr void add(size_t hash, int index)
    {
        Entry entry{hash, index};
        while (true)
        {
            auto target_slot = compute_slot(entry.hash);
            for (auto slot = target_slot; slot < m_entries.size(); ++slot)
            {
                if (m_entries[slot].index == -1)
                {
                    m_entries[slot] = entry;
                    return;
                }

                // Robin hood hashing
                auto candidate_slot = compute_slot(m_entries[slot].hash);
                if (target_slot < candidate_slot)
                {
                    constexpr_swap(m_entries[slot], entry);
                    target_slot = candidate_slot;
                }
            }
            // no free entries found, resize, try again
            resize(m_entries.size() * 2);
        }
    }

    constexpr void remove(size_t hash, int index)
    {
        for (auto slot = compute_slot(hash); slot < m_entries.size(); ++slot)
        {
            kak_assert(m_entries[slot].index >= 0);
            if (m_entries[slot].index == index)
            {
                m_entries[slot].index = -1;
                // Recompact following entries
                for (auto next = slot+1; next < m_entries.size(); ++next)
                {
                    if (m_entries[next].index == -1 or
                        compute_slot(m_entries[next].hash) == next)
                        break;
                    kak_assert(compute_slot(m_entries[next].hash) < next);
                    constexpr_swap(m_entries[next-1], m_entries[next]);
                }
                break;
            }
        }
    }

    constexpr void ordered_fix_entries(int index)
    {
        for (auto& entry : m_entries)
        {
            if (entry.index >= index)
                --entry.index;
        }
    }

    constexpr void unordered_fix_entries(size_t hash, int old_index, int new_index)
    {
        for (auto slot = compute_slot(hash); slot < m_entries.size(); ++slot)
        {
            if (m_entries[slot].index == old_index)
            {
                m_entries[slot].index = new_index;
                return;
            }
        }
        kak_assert(false); // entry not found ?!
    }

    constexpr const Entry& operator[](size_t index) const { return m_entries[index]; }
    constexpr size_t size() const { return m_entries.size(); }
    constexpr size_t compute_slot(size_t hash) const
    {
        // We assume entries.size() is power of 2
        return hash & (m_entries.size()-1);
    }

    constexpr void clear() { m_entries.clear(); }

private:
    ContainerType m_entries;
};

template<typename Key, typename Value>
struct HashItem
{
    Key key;
    Value value;
};

template<typename Key, typename Value,
         MemoryDomain domain = MemoryDomain::Undefined,
         template<typename, MemoryDomain> class Container = Vector>
struct HashMap
{
    using Item = HashItem<Key, Value>;
    using ContainerType = Container<Item, domain>;

    constexpr HashMap() = default;

    constexpr HashMap(std::initializer_list<Item> val) : m_items(val), m_index(val.size())
    {
        for (int i = 0; i < m_items.size(); ++i)
            m_index.add(hash_value(m_items[i].key), i);
    }

    constexpr Value& insert(Item item)
    {
        m_index.reserve(m_items.size()+1);
        m_index.add(hash_value(item.key), (int)m_items.size());
        m_items.push_back(std::move(item));
        return m_items.back().value;
    }

    template<typename KeyType>
    using EnableIfHashCompatible = std::enable_if_t<
        IsHashCompatible<Key, std::decay_t<KeyType>>
    >;

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr int find_index(const KeyType& key, size_t hash) const
    {
        for (auto slot = m_index.compute_slot(hash); slot < m_index.size(); ++slot)
        {
            auto& entry = m_index[slot];
            if (entry.index == -1)
                return -1;
            if (entry.hash == hash and m_items[entry.index].key == key)
                return entry.index;
        }
        return -1;
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr int find_index(const KeyType& key) const { return find_index(key, hash_value(key)); }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr bool contains(const KeyType& key) const { return find_index(key) >= 0; }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr Value& operator[](KeyType&& key)
    {
        const auto hash = hash_value(key);
        auto index = find_index(key, hash);
        if (index >= 0)
            return m_items[index].value;

        m_index.reserve(m_items.size()+1);
        m_index.add(hash, (int)m_items.size());
        m_items.push_back({Key(std::forward<KeyType>(key)), {}});
        return m_items.back().value;
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr void remove(const KeyType& key)
    {
        const auto hash = hash_value(key);
        int index = find_index(key, hash);
        if (index >= 0)
        {
            m_items.erase(m_items.begin() + index);
            m_index.remove(hash, index);
            m_index.ordered_fix_entries(index);
        }
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr void unordered_remove(const KeyType& key)
    {
        const auto hash = hash_value(key);
        int index = find_index(key, hash);
        if (index >= 0)
        {
            constexpr_swap(m_items[index], m_items.back());
            m_items.pop_back();
            m_index.remove(hash, index);
            if (index != m_items.size())
                m_index.unordered_fix_entries(hash_value(m_items[index].key), m_items.size(), index);
        }
    }

    constexpr void erase(const Key& key) { unordered_remove(key); }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr void remove_all(const KeyType& key)
    {
        const auto hash = hash_value(key);
        for (int index = find_index(key, hash); index >= 0;
             index = find_index(key, hash))
        {
            m_items.erase(m_items.begin() + index);
            m_index.remove(hash, index);
            m_index.ordered_fix_entries(index);
        }
    }

    using iterator = typename ContainerType::iterator;
    constexpr iterator begin() { return m_items.begin(); }
    constexpr iterator end() { return m_items.end(); }

    using const_iterator = typename ContainerType::const_iterator;
    constexpr const_iterator begin() const { return m_items.begin(); }
    constexpr const_iterator end() const { return m_items.end(); }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr iterator find(const KeyType& key)
    {
        auto index = find_index(key);
        return index >= 0 ? begin() + index : end();
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr const_iterator find(const KeyType& key) const
    {
        return const_cast<HashMap*>(this)->find(key);
    }

    constexpr void clear() { m_items.clear(); m_index.clear(); }

    constexpr size_t size() const { return m_items.size(); }
    constexpr bool empty() const { return m_items.empty(); }
    constexpr void reserve(size_t size)
    {
        m_items.reserve(size);
        m_index.reserve(size);
    }

    // Equality is taking the order of insertion into account
    template<MemoryDomain otherDomain>
    constexpr bool operator==(const HashMap<Key, Value, otherDomain, Container>& other) const
    {
        return size() == other.size() and
            std::equal(begin(), end(), other.begin(),
                       [](const Item& lhs, const Item& rhs) {
                           return lhs.key == rhs.key and lhs.value == rhs.value;
                       });
    }

    template<MemoryDomain otherDomain>
    constexpr bool operator!=(const HashMap<Key, Value, otherDomain, Container>& other) const
    {
        return not (*this == other);
    }

private:
    ContainerType m_items;
    HashIndex<domain, Container> m_index;
};

void profile_hash_maps();

}

#endif // hash_map_hh_INCLUDED
<commit_msg>HashMap: Tolerate reserving for 0 elements<commit_after>#ifndef hash_map_hh_INCLUDED
#define hash_map_hh_INCLUDED

#include "hash.hh"
#include "memory.hh"
#include "vector.hh"

namespace Kakoune
{

template<typename T>
constexpr void constexpr_swap(T& lhs, T& rhs)
{
    T tmp = std::move(lhs);
    lhs = std::move(rhs);
    rhs = std::move(tmp);
}

template<MemoryDomain domain,
         template<typename, MemoryDomain> class Container>
struct HashIndex
{
    struct Entry
    {
        size_t hash = 0;
        int index = -1;
    };

    static constexpr float max_fill_rate = 0.5f;

    constexpr HashIndex() = default;
    constexpr HashIndex(size_t count)
    {
        const size_t min_size = (size_t)(count / max_fill_rate) + 1;
        size_t new_size = 4;
        while (new_size < min_size)
            new_size *= 2;
        m_entries.resize(new_size, {});
    }

    using ContainerType = Container<Entry, domain>;

    constexpr void resize(size_t new_size)
    {
        kak_assert(new_size > m_entries.size());
        ContainerType old_entries = std::move(m_entries);
        m_entries.resize(new_size, {});
        for (auto& entry : old_entries)
        {
            if (entry.index >= 0)
                add(entry.hash, entry.index);
        }
    }

    constexpr void reserve(size_t count)
    {
        if (count == 0)
            return;

        const size_t min_size = (size_t)(count / max_fill_rate) + 1;
        size_t new_size = m_entries.empty() ? 4 : m_entries.size();
        while (new_size < min_size)
            new_size *= 2;

        if (new_size > m_entries.size())
            resize(new_size);
    }

    constexpr void add(size_t hash, int index)
    {
        Entry entry{hash, index};
        while (true)
        {
            auto target_slot = compute_slot(entry.hash);
            for (auto slot = target_slot; slot < m_entries.size(); ++slot)
            {
                if (m_entries[slot].index == -1)
                {
                    m_entries[slot] = entry;
                    return;
                }

                // Robin hood hashing
                auto candidate_slot = compute_slot(m_entries[slot].hash);
                if (target_slot < candidate_slot)
                {
                    constexpr_swap(m_entries[slot], entry);
                    target_slot = candidate_slot;
                }
            }
            // no free entries found, resize, try again
            resize(m_entries.size() * 2);
        }
    }

    constexpr void remove(size_t hash, int index)
    {
        for (auto slot = compute_slot(hash); slot < m_entries.size(); ++slot)
        {
            kak_assert(m_entries[slot].index >= 0);
            if (m_entries[slot].index == index)
            {
                m_entries[slot].index = -1;
                // Recompact following entries
                for (auto next = slot+1; next < m_entries.size(); ++next)
                {
                    if (m_entries[next].index == -1 or
                        compute_slot(m_entries[next].hash) == next)
                        break;
                    kak_assert(compute_slot(m_entries[next].hash) < next);
                    constexpr_swap(m_entries[next-1], m_entries[next]);
                }
                break;
            }
        }
    }

    constexpr void ordered_fix_entries(int index)
    {
        for (auto& entry : m_entries)
        {
            if (entry.index >= index)
                --entry.index;
        }
    }

    constexpr void unordered_fix_entries(size_t hash, int old_index, int new_index)
    {
        for (auto slot = compute_slot(hash); slot < m_entries.size(); ++slot)
        {
            if (m_entries[slot].index == old_index)
            {
                m_entries[slot].index = new_index;
                return;
            }
        }
        kak_assert(false); // entry not found ?!
    }

    constexpr const Entry& operator[](size_t index) const { return m_entries[index]; }
    constexpr size_t size() const { return m_entries.size(); }
    constexpr size_t compute_slot(size_t hash) const
    {
        // We assume entries.size() is power of 2
        return hash & (m_entries.size()-1);
    }

    constexpr void clear() { m_entries.clear(); }

private:
    ContainerType m_entries;
};

template<typename Key, typename Value>
struct HashItem
{
    Key key;
    Value value;
};

template<typename Key, typename Value,
         MemoryDomain domain = MemoryDomain::Undefined,
         template<typename, MemoryDomain> class Container = Vector>
struct HashMap
{
    using Item = HashItem<Key, Value>;
    using ContainerType = Container<Item, domain>;

    constexpr HashMap() = default;

    constexpr HashMap(std::initializer_list<Item> val) : m_items(val), m_index(val.size())
    {
        for (int i = 0; i < m_items.size(); ++i)
            m_index.add(hash_value(m_items[i].key), i);
    }

    constexpr Value& insert(Item item)
    {
        m_index.reserve(m_items.size()+1);
        m_index.add(hash_value(item.key), (int)m_items.size());
        m_items.push_back(std::move(item));
        return m_items.back().value;
    }

    template<typename KeyType>
    using EnableIfHashCompatible = std::enable_if_t<
        IsHashCompatible<Key, std::decay_t<KeyType>>
    >;

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr int find_index(const KeyType& key, size_t hash) const
    {
        for (auto slot = m_index.compute_slot(hash); slot < m_index.size(); ++slot)
        {
            auto& entry = m_index[slot];
            if (entry.index == -1)
                return -1;
            if (entry.hash == hash and m_items[entry.index].key == key)
                return entry.index;
        }
        return -1;
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr int find_index(const KeyType& key) const { return find_index(key, hash_value(key)); }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr bool contains(const KeyType& key) const { return find_index(key) >= 0; }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr Value& operator[](KeyType&& key)
    {
        const auto hash = hash_value(key);
        auto index = find_index(key, hash);
        if (index >= 0)
            return m_items[index].value;

        m_index.reserve(m_items.size()+1);
        m_index.add(hash, (int)m_items.size());
        m_items.push_back({Key(std::forward<KeyType>(key)), {}});
        return m_items.back().value;
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr void remove(const KeyType& key)
    {
        const auto hash = hash_value(key);
        int index = find_index(key, hash);
        if (index >= 0)
        {
            m_items.erase(m_items.begin() + index);
            m_index.remove(hash, index);
            m_index.ordered_fix_entries(index);
        }
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr void unordered_remove(const KeyType& key)
    {
        const auto hash = hash_value(key);
        int index = find_index(key, hash);
        if (index >= 0)
        {
            constexpr_swap(m_items[index], m_items.back());
            m_items.pop_back();
            m_index.remove(hash, index);
            if (index != m_items.size())
                m_index.unordered_fix_entries(hash_value(m_items[index].key), m_items.size(), index);
        }
    }

    constexpr void erase(const Key& key) { unordered_remove(key); }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr void remove_all(const KeyType& key)
    {
        const auto hash = hash_value(key);
        for (int index = find_index(key, hash); index >= 0;
             index = find_index(key, hash))
        {
            m_items.erase(m_items.begin() + index);
            m_index.remove(hash, index);
            m_index.ordered_fix_entries(index);
        }
    }

    using iterator = typename ContainerType::iterator;
    constexpr iterator begin() { return m_items.begin(); }
    constexpr iterator end() { return m_items.end(); }

    using const_iterator = typename ContainerType::const_iterator;
    constexpr const_iterator begin() const { return m_items.begin(); }
    constexpr const_iterator end() const { return m_items.end(); }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr iterator find(const KeyType& key)
    {
        auto index = find_index(key);
        return index >= 0 ? begin() + index : end();
    }

    template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
    constexpr const_iterator find(const KeyType& key) const
    {
        return const_cast<HashMap*>(this)->find(key);
    }

    constexpr void clear() { m_items.clear(); m_index.clear(); }

    constexpr size_t size() const { return m_items.size(); }
    constexpr bool empty() const { return m_items.empty(); }
    constexpr void reserve(size_t size)
    {
        m_items.reserve(size);
        m_index.reserve(size);
    }

    // Equality is taking the order of insertion into account
    template<MemoryDomain otherDomain>
    constexpr bool operator==(const HashMap<Key, Value, otherDomain, Container>& other) const
    {
        return size() == other.size() and
            std::equal(begin(), end(), other.begin(),
                       [](const Item& lhs, const Item& rhs) {
                           return lhs.key == rhs.key and lhs.value == rhs.value;
                       });
    }

    template<MemoryDomain otherDomain>
    constexpr bool operator!=(const HashMap<Key, Value, otherDomain, Container>& other) const
    {
        return not (*this == other);
    }

private:
    ContainerType m_items;
    HashIndex<domain, Container> m_index;
};

void profile_hash_maps();

}

#endif // hash_map_hh_INCLUDED
<|endoftext|>


<commit_before>/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2007 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <windows.h>

#ifdef _DEBUG
#  include <iostream>
#endif

#include <dinput.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <assert.h>

#include <drivers/Microsoft/DirectXJoystick/DirectXJoystickExceptions.h>
#include <drivers/Microsoft/DirectXJoystick/DirectXJoystickStandalone.h>

namespace gadget
{

BOOL CALLBACK enumerateJoysticksCallback(const DIDEVICEINSTANCE* dInstance,
                                         void* pContext)
{
   DirectXJoystickStandalone* obj =
      static_cast<DirectXJoystickStandalone*>(pContext);
   return obj->enumerateJoysticks(dInstance);
}

BOOL CALLBACK enumerateAxesCallback(const DIDEVICEOBJECTINSTANCE* doi,
                                    void* pContext)
{
   DirectXJoystickStandalone* obj =
      static_cast<DirectXJoystickStandalone*>(pContext);
   return obj->enumerateAxes(doi);
}

DirectXJoystickStandalone::DirectXJoystickStandalone()
   : mDxObject(NULL)
   , mDxJoystick(NULL)
   , mDataFormatObj(&c_dfDIJoystick)
   , mDataFormatSize(sizeof(mJsData))
{
}

void DirectXJoystickStandalone::init()
{
   HRESULT status;

   // Create a Direct Input manager object.
   status = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
                               IID_IDirectInput8, (VOID**) &mDxObject, NULL);

   // An error returned at this point indicates that Direct Input is not
   // available.  That means that we are effectively done with this device.
   if ( FAILED(status) )
   {
      throw DirectXJoystickException("Direct Input is not available");
   }

   // Look for joystick.
   mDxObject->EnumDevices(DI8DEVCLASS_GAMECTRL, enumerateJoysticksCallback,
                          (void*) this, (DWORD) DIEDFL_ATTACHEDONLY);

   status = mDxJoystick->SetDataFormat(mDataFormatObj);
   if ( FAILED(status) )
   {
      if ( status ==  DIERR_ACQUIRED )
      {
         throw DirectXJoystickDataFormatException("Cannot set data format while device is acquired");
      }
      else if ( status == DIERR_INVALIDPARAM )
      {
         throw DirectXJoystickDataFormatException("Invalid data format chosen");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickDataFormatException("Cannot set data format on uninitialized device");
      }
      else
      {
         throw DirectXJoystickDataFormatException("Unknown error when trying to set the data format");
      }
   }

   // Set the cooperative level to let DInput know how this device should
   // interact with the system and with other DInput applications.
   // NOTE: This always fails because the first parameter is NULL!
   status =
      mDxJoystick->SetCooperativeLevel((HWND) NULL,
                                       DISCL_EXCLUSIVE | DISCL_FOREGROUND);

/*
   if ( FAILED(status) )
   {
      if ( status == DIERR_INVALIDPARAM )
      {
         throw DirectXJoystickCooperationException("Invalid cooperative mode chosen");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickCooperationException("Cannot set cooperative mode on uninitialized device");
      }
      else if ( status == E_HANDLE )
      {
         throw DirectXJoystickCooperationException("Invalid HWND used when setting cooperative mode");
      }
      else
      {
         throw DirectXJoystickCooperationException("Unknown error when trying to set the cooperative mode");
      }
   }
*/

   mCapabilities.dwSize = sizeof(DIDEVCAPS);
   status = mDxJoystick->GetCapabilities(&mCapabilities);

   if ( FAILED(status) )
   {
      if ( status == DIERR_INVALIDPARAM || status == E_POINTER )
      {
         throw DirectXJoystickQueryException("Invalid data passed to IDirectInputDevice8::GetCapabilities()");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickQueryException("Cannot query capabilities for an uninitialized device");
      }
      else
      {
         throw DirectXJoystickQueryException("Unknown error when trying to query the joystick capabilities");
      }
   }

   mAxisValueMap.resize(mCapabilities.dwAxes, NULL);

   // Enumerate the axes of the joystick and set the range of each axis found.
   mDxJoystick->EnumObjects(enumerateAxesCallback, (void*) this, DIDFT_AXIS);

   status = mDxJoystick->Acquire();

   // DI_OK indicates that we acquired the device successfully.
   // S_FALSE indicates that we had already acquired the device.
   if ( status != DI_OK && status != S_FALSE )
   {
      if ( status == DIERR_INVALIDPARAM )
      {
         throw DirectXJoystickDataFormatException("Invalid parameter passed during device acquisition");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickDataFormatException("Cannot acquire an uninitialized device");
      }
      else if ( status == DIERR_OTHERAPPHASPRIO )
      {
         throw DirectXJoystickDataFormatException("A higher priority application has already acquired the device");
      }
      else
      {
         throw DirectXJoystickDataFormatException("Unknown error when trying to acquire the device");
      }
   }

   // Get device-specific information.
   DIDEVICEINSTANCE dx_dev_info;
   dx_dev_info.dwSize = sizeof(DIDEVICEINSTANCE);
   mDxJoystick->GetDeviceInfo(&dx_dev_info);
   mType = dx_dev_info.dwDevType;
   mProductName = std::string(dx_dev_info.tszProductName);
   if ( mProductName.empty() )
   {
      mProductName = "unknown";
   }
}

void DirectXJoystickStandalone::close()
{
   // Unacquire & release any DirectInputDevice objects.
   if ( mDxJoystick != NULL )
   {
      mDxJoystick->Unacquire();
      mDxJoystick->Release();
      mDxJoystick = NULL;
   }

   // Release any DirectInput objects.
   if ( mDxObject != NULL )
   {
      mDxObject->Release();
      mDxObject = NULL;
   }
}

bool DirectXJoystickStandalone::poll()
{
   bool sample_taken(false);

   HRESULT status = mDxJoystick->Poll();

   // If we failed to poll the joystick, try to reacquire it.  When we cannot
   // poll the device, we cannot take a sample.
   if ( FAILED(status) )
   {
      status = mDxJoystick->Acquire();
      // XXX: It might be necessary to put a limit on the number of times we
      // try to reacquire the device as this process could hold up the Input
      // Manager.
      while ( status == DIERR_INPUTLOST )
      {
         status = mDxJoystick->Acquire();
      }
   }
   // We polled the joystick successfully, so we can read a sample.
   else
   {
      status = mDxJoystick->GetDeviceState(mDataFormatSize, &mJsData);
      sample_taken = true;
   }

   return sample_taken;
}

const DIJOYSTATE& DirectXJoystickStandalone::getData() const
{
   return mJsData;
}

DWORD DirectXJoystickStandalone::getType() const
{
   return mType;
}

const std::string& DirectXJoystickStandalone::getProductName() const
{
   return mProductName;
}

LONG DirectXJoystickStandalone::getAxisValue(const unsigned int axisIndex)
{
   if ( axisIndex < mCapabilities.dwAxes )
   {
      return *(mAxisValueMap[axisIndex]);
   }
   else
   {
      // Map the given axis index to the range [0,3] so that we can use
      // the result as the index into mJsData.rgdwPOV.
      const unsigned int pov_index((axisIndex - mCapabilities.dwAxes) / 2);
      const DWORD pov_value(mJsData.rgdwPOV[pov_index]);

      // The hat is centered.
      if ( (LOWORD(pov_value) == 0xffff) )
      {
         return (getAxisMax() + getAxisMin()) / 2;
      }
      else
      {
         const float pi = 3.14159265358979323846f;
         const float pov_rads((pov_value / 100.0) * pi / 180.0f);

         // We treat an even-numbered axis index as an X axis request.
         // An odd-numbered axis index then a Y axis request.
         const bool x_axis((axisIndex % 2) == 0);

         return (x_axis ? getAxisMax() * sinf(pov_rads)
                        : getAxisMax() * cosf(pov_rads));
      }
   }
}

BOOL DirectXJoystickStandalone::enumerateJoysticks(const DIDEVICEINSTANCE* dInstance)
{
   // Obtain an interface to the enumerated joystick.
   HRESULT hr = mDxObject->CreateDevice(dInstance->guidInstance, &mDxJoystick,
                                        NULL);
   return (FAILED(hr) ? DIENUM_CONTINUE : DIENUM_STOP);
/* // FIXME: allow configurable joystick station
   static int count = 0;
   ++count;
   if( count == 2 )
   {
      mDxObject->CreateDevice(dInstance->guidInstance, &mDxJoystick, NULL);
      return DIENUM_STOP;
   }
   return DIENUM_CONTINUE;
*/
}

BOOL DirectXJoystickStandalone::enumerateAxes(const DIDEVICEOBJECTINSTANCE* doi)
{
   // The axes appear to be enumerated in reverse order by Direct Input.
   // XXX: Can we depend on this?  Using DIDFT_GETINSTANCE(doi->dwType) gives
   // back values that I would not expect in certain cases.  In other words,
   // the object instance number does not correlate with the memory ordering
   // in DIJOYSTATE.
   static int axis_index(mCapabilities.dwAxes - 1);
   assert(axis_index >= 0 && "axis_index went negative!");

   // For each axis enumrated, this function will set
   // the minimum and maximum range values for it.
   if ( doi->dwType & DIDFT_AXIS )
   {
      DIPROPRANGE diprg;
      diprg.diph.dwSize       = sizeof(DIPROPRANGE);
      diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
      diprg.diph.dwHow        = DIPH_BYID;
      diprg.diph.dwObj        = doi->dwType;
      diprg.lMin              = getAxisMin();
      diprg.lMax              = getAxisMax();

      // Set the range for the current axis.
      if( FAILED(mDxJoystick->SetProperty(DIPROP_RANGE, &diprg.diph)) )
      {
         return DIENUM_STOP;
      }

      LONG* value_addr(NULL);
      if ( doi->guidType == GUID_XAxis )
      {
         value_addr = &mJsData.lX;
#ifdef _DEBUG
         std::cout << "Object for X axis" << std::endl;
#endif
      }
      else if ( doi->guidType == GUID_YAxis )
      {
         value_addr = &mJsData.lY;
#ifdef _DEBUG
         std::cout << "Object for Y axis" << std::endl;
#endif
      }
      else if ( doi->guidType == GUID_ZAxis )
      {
         value_addr = &mJsData.lZ;
#ifdef _DEBUG
         std::cout << "Object for Z axis" << std::endl;
#endif
      }
      else if ( doi->guidType == GUID_RxAxis )
      {
         value_addr = &mJsData.lRx;
#ifdef _DEBUG
         std::cout << "Object for X axis rotation" << std::endl;
#endif
      }
      else if ( doi->guidType == GUID_RyAxis )
      {
         value_addr = &mJsData.lRy;
#ifdef _DEBUG
         std::cout << "Object for Y axis rotation" << std::endl;
#endif
      }
      else if ( doi->guidType == GUID_RzAxis )
      {
         value_addr = &mJsData.lRz;
#ifdef _DEBUG
         std::cout << "Object for Z axis rotation" << std::endl;
#endif
      }
      else if ( doi->guidType == GUID_Slider )
      {
         // XXX: I do not know the correct way to distinguish between the two
         // possible sliders.  -PH 2/2/2005
         if ( DIDFT_GETINSTANCE(doi->dwType) == 2 )
         {
            value_addr = &mJsData.rglSlider[0];
         }
         else
         {
            value_addr = &mJsData.rglSlider[1];
         }
#ifdef _DEBUG
         std::cout << "Object for slider" << std::endl;
#endif
      }

#ifdef _DEBUG
      std::cout << "Setting mAxisValueMap[" << axis_index << "]" << std::endl;
#endif
      mAxisValueMap[axis_index] = value_addr;
      axis_index--;
   }

   return DIENUM_CONTINUE;
}

} // End of gadget namespace
<commit_msg>Removed debug output.<commit_after>/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2007 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <windows.h>

#include <dinput.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <assert.h>

#include <drivers/Microsoft/DirectXJoystick/DirectXJoystickExceptions.h>
#include <drivers/Microsoft/DirectXJoystick/DirectXJoystickStandalone.h>

namespace gadget
{

BOOL CALLBACK enumerateJoysticksCallback(const DIDEVICEINSTANCE* dInstance,
                                         void* pContext)
{
   DirectXJoystickStandalone* obj =
      static_cast<DirectXJoystickStandalone*>(pContext);
   return obj->enumerateJoysticks(dInstance);
}

BOOL CALLBACK enumerateAxesCallback(const DIDEVICEOBJECTINSTANCE* doi,
                                    void* pContext)
{
   DirectXJoystickStandalone* obj =
      static_cast<DirectXJoystickStandalone*>(pContext);
   return obj->enumerateAxes(doi);
}

DirectXJoystickStandalone::DirectXJoystickStandalone()
   : mDxObject(NULL)
   , mDxJoystick(NULL)
   , mDataFormatObj(&c_dfDIJoystick)
   , mDataFormatSize(sizeof(mJsData))
{
}

void DirectXJoystickStandalone::init()
{
   HRESULT status;

   // Create a Direct Input manager object.
   status = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
                               IID_IDirectInput8, (VOID**) &mDxObject, NULL);

   // An error returned at this point indicates that Direct Input is not
   // available.  That means that we are effectively done with this device.
   if ( FAILED(status) )
   {
      throw DirectXJoystickException("Direct Input is not available");
   }

   // Look for joystick.
   mDxObject->EnumDevices(DI8DEVCLASS_GAMECTRL, enumerateJoysticksCallback,
                          (void*) this, (DWORD) DIEDFL_ATTACHEDONLY);

   status = mDxJoystick->SetDataFormat(mDataFormatObj);
   if ( FAILED(status) )
   {
      if ( status ==  DIERR_ACQUIRED )
      {
         throw DirectXJoystickDataFormatException("Cannot set data format while device is acquired");
      }
      else if ( status == DIERR_INVALIDPARAM )
      {
         throw DirectXJoystickDataFormatException("Invalid data format chosen");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickDataFormatException("Cannot set data format on uninitialized device");
      }
      else
      {
         throw DirectXJoystickDataFormatException("Unknown error when trying to set the data format");
      }
   }

   // Set the cooperative level to let DInput know how this device should
   // interact with the system and with other DInput applications.
   // NOTE: This always fails because the first parameter is NULL!
   status =
      mDxJoystick->SetCooperativeLevel((HWND) NULL,
                                       DISCL_EXCLUSIVE | DISCL_FOREGROUND);

/*
   if ( FAILED(status) )
   {
      if ( status == DIERR_INVALIDPARAM )
      {
         throw DirectXJoystickCooperationException("Invalid cooperative mode chosen");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickCooperationException("Cannot set cooperative mode on uninitialized device");
      }
      else if ( status == E_HANDLE )
      {
         throw DirectXJoystickCooperationException("Invalid HWND used when setting cooperative mode");
      }
      else
      {
         throw DirectXJoystickCooperationException("Unknown error when trying to set the cooperative mode");
      }
   }
*/

   mCapabilities.dwSize = sizeof(DIDEVCAPS);
   status = mDxJoystick->GetCapabilities(&mCapabilities);

   if ( FAILED(status) )
   {
      if ( status == DIERR_INVALIDPARAM || status == E_POINTER )
      {
         throw DirectXJoystickQueryException("Invalid data passed to IDirectInputDevice8::GetCapabilities()");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickQueryException("Cannot query capabilities for an uninitialized device");
      }
      else
      {
         throw DirectXJoystickQueryException("Unknown error when trying to query the joystick capabilities");
      }
   }

   mAxisValueMap.resize(mCapabilities.dwAxes, NULL);

   // Enumerate the axes of the joystick and set the range of each axis found.
   mDxJoystick->EnumObjects(enumerateAxesCallback, (void*) this, DIDFT_AXIS);

   status = mDxJoystick->Acquire();

   // DI_OK indicates that we acquired the device successfully.
   // S_FALSE indicates that we had already acquired the device.
   if ( status != DI_OK && status != S_FALSE )
   {
      if ( status == DIERR_INVALIDPARAM )
      {
         throw DirectXJoystickDataFormatException("Invalid parameter passed during device acquisition");
      }
      else if ( status == DIERR_NOTINITIALIZED )
      {
         throw DirectXJoystickDataFormatException("Cannot acquire an uninitialized device");
      }
      else if ( status == DIERR_OTHERAPPHASPRIO )
      {
         throw DirectXJoystickDataFormatException("A higher priority application has already acquired the device");
      }
      else
      {
         throw DirectXJoystickDataFormatException("Unknown error when trying to acquire the device");
      }
   }

   // Get device-specific information.
   DIDEVICEINSTANCE dx_dev_info;
   dx_dev_info.dwSize = sizeof(DIDEVICEINSTANCE);
   mDxJoystick->GetDeviceInfo(&dx_dev_info);
   mType = dx_dev_info.dwDevType;
   mProductName = std::string(dx_dev_info.tszProductName);
   if ( mProductName.empty() )
   {
      mProductName = "unknown";
   }
}

void DirectXJoystickStandalone::close()
{
   // Unacquire & release any DirectInputDevice objects.
   if ( mDxJoystick != NULL )
   {
      mDxJoystick->Unacquire();
      mDxJoystick->Release();
      mDxJoystick = NULL;
   }

   // Release any DirectInput objects.
   if ( mDxObject != NULL )
   {
      mDxObject->Release();
      mDxObject = NULL;
   }
}

bool DirectXJoystickStandalone::poll()
{
   bool sample_taken(false);

   HRESULT status = mDxJoystick->Poll();

   // If we failed to poll the joystick, try to reacquire it.  When we cannot
   // poll the device, we cannot take a sample.
   if ( FAILED(status) )
   {
      status = mDxJoystick->Acquire();
      // XXX: It might be necessary to put a limit on the number of times we
      // try to reacquire the device as this process could hold up the Input
      // Manager.
      while ( status == DIERR_INPUTLOST )
      {
         status = mDxJoystick->Acquire();
      }
   }
   // We polled the joystick successfully, so we can read a sample.
   else
   {
      status = mDxJoystick->GetDeviceState(mDataFormatSize, &mJsData);
      sample_taken = true;
   }

   return sample_taken;
}

const DIJOYSTATE& DirectXJoystickStandalone::getData() const
{
   return mJsData;
}

DWORD DirectXJoystickStandalone::getType() const
{
   return mType;
}

const std::string& DirectXJoystickStandalone::getProductName() const
{
   return mProductName;
}

LONG DirectXJoystickStandalone::getAxisValue(const unsigned int axisIndex)
{
   if ( axisIndex < mCapabilities.dwAxes )
   {
      return *(mAxisValueMap[axisIndex]);
   }
   else
   {
      // Map the given axis index to the range [0,3] so that we can use
      // the result as the index into mJsData.rgdwPOV.
      const unsigned int pov_index((axisIndex - mCapabilities.dwAxes) / 2);
      const DWORD pov_value(mJsData.rgdwPOV[pov_index]);

      // The hat is centered.
      if ( (LOWORD(pov_value) == 0xffff) )
      {
         return (getAxisMax() + getAxisMin()) / 2;
      }
      else
      {
         const float pi = 3.14159265358979323846f;
         const float pov_rads((pov_value / 100.0) * pi / 180.0f);

         // We treat an even-numbered axis index as an X axis request.
         // An odd-numbered axis index then a Y axis request.
         const bool x_axis((axisIndex % 2) == 0);

         return (x_axis ? getAxisMax() * sinf(pov_rads)
                        : getAxisMax() * cosf(pov_rads));
      }
   }
}

BOOL DirectXJoystickStandalone::enumerateJoysticks(const DIDEVICEINSTANCE* dInstance)
{
   // Obtain an interface to the enumerated joystick.
   HRESULT hr = mDxObject->CreateDevice(dInstance->guidInstance, &mDxJoystick,
                                        NULL);
   return (FAILED(hr) ? DIENUM_CONTINUE : DIENUM_STOP);
/* // FIXME: allow configurable joystick station
   static int count = 0;
   ++count;
   if( count == 2 )
   {
      mDxObject->CreateDevice(dInstance->guidInstance, &mDxJoystick, NULL);
      return DIENUM_STOP;
   }
   return DIENUM_CONTINUE;
*/
}

BOOL DirectXJoystickStandalone::enumerateAxes(const DIDEVICEOBJECTINSTANCE* doi)
{
   // The axes appear to be enumerated in reverse order by Direct Input.
   // XXX: Can we depend on this?  Using DIDFT_GETINSTANCE(doi->dwType) gives
   // back values that I would not expect in certain cases.  In other words,
   // the object instance number does not correlate with the memory ordering
   // in DIJOYSTATE.
   static int axis_index(mCapabilities.dwAxes - 1);
   assert(axis_index >= 0 && "axis_index went negative!");

   // For each axis enumrated, this function will set
   // the minimum and maximum range values for it.
   if ( doi->dwType & DIDFT_AXIS )
   {
      DIPROPRANGE diprg;
      diprg.diph.dwSize       = sizeof(DIPROPRANGE);
      diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
      diprg.diph.dwHow        = DIPH_BYID;
      diprg.diph.dwObj        = doi->dwType;
      diprg.lMin              = getAxisMin();
      diprg.lMax              = getAxisMax();

      // Set the range for the current axis.
      if( FAILED(mDxJoystick->SetProperty(DIPROP_RANGE, &diprg.diph)) )
      {
         return DIENUM_STOP;
      }

      LONG* value_addr(NULL);
      if ( doi->guidType == GUID_XAxis )
      {
         value_addr = &mJsData.lX;
      }
      else if ( doi->guidType == GUID_YAxis )
      {
         value_addr = &mJsData.lY;
      }
      else if ( doi->guidType == GUID_ZAxis )
      {
         value_addr = &mJsData.lZ;
      }
      else if ( doi->guidType == GUID_RxAxis )
      {
         value_addr = &mJsData.lRx;
      }
      else if ( doi->guidType == GUID_RyAxis )
      {
         value_addr = &mJsData.lRy;
      }
      else if ( doi->guidType == GUID_RzAxis )
      {
         value_addr = &mJsData.lRz;
      }
      else if ( doi->guidType == GUID_Slider )
      {
         // XXX: I do not know the correct way to distinguish between the two
         // possible sliders.  -PH 2/2/2005
         if ( DIDFT_GETINSTANCE(doi->dwType) == 2 )
         {
            value_addr = &mJsData.rglSlider[0];
         }
         else
         {
            value_addr = &mJsData.rglSlider[1];
         }
      }

      mAxisValueMap[axis_index] = value_addr;
      axis_index--;
   }

   return DIENUM_CONTINUE;
}

} // End of gadget namespace
<|endoftext|>


<commit_before>/** \file   SimpleXmlParser.cc
 *  \brief  A non-validating XML parser class.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "SimpleXmlParser.h"
#include <stdexcept>
#include "Compiler.h"
#include "StringUtil.h"
#include "TextUtil.h"

namespace {

inline bool DecodeEnity(const std::string &entity_string, std::string * const decoded_char) {
    if (unlikely(entity_string.empty()))
        return false;

    if (__builtin_strcmp(entity_string.c_str(), "amp") == 0)
        *decoded_char = "&";
    else if (__builtin_strcmp(entity_string.c_str(), "apos") == 0)
        *decoded_char = "'";
    else if (__builtin_strcmp(entity_string.c_str(), "quot") == 0)
        *decoded_char = "\"";
    else if (__builtin_strcmp(entity_string.c_str(), "lt") == 0)
        *decoded_char = "<";
    else if (__builtin_strcmp(entity_string.c_str(), "gt") == 0)
        *decoded_char = ">";
    else if (unlikely(entity_string[0] == '#')) {
        if (entity_string.length() < 2)
            return false;

        unsigned code_point;
        if (entity_string[1] == 'x') {
            if (entity_string.length() < 3 or entity_string.length() > 6)
                return false;
            if (not StringUtil::ToUnsigned(entity_string.substr(2), &code_point, 16))
                return false;
        } else {
            if (entity_string.length() < 2 or entity_string.length() > 6)
                return false;
            if (not StringUtil::ToUnsigned(entity_string.substr(1), &code_point))
                return false;
        }

        if (not TextUtil::WCharToUTF8String(static_cast<wchar_t>(code_point), decoded_char))
            return false;
    } else
        return false;

    return true;
}

// Replaces entities in-place and resizes "data", if necessary.
inline bool DecodeEntities(std::string * const data) {
    if (unlikely(data->empty()))
        return true;

    std::string::iterator next(data->begin());
    bool in_entity(false);
    std::string entity;
    for (const auto ch : *data) {
        if (unlikely(in_entity)) {
            if (ch == ';') {
                std::string decoded_char;
                if (not DecodeEnity(entity, &decoded_char))
                    return false;
                for (const auto dch : decoded_char)
                    *next++ = dch;
                in_entity = false;
            } else
                entity += ch;
        } else if (unlikely(ch == '&')) {
            in_entity = true;
            entity.clear();
        } else
            *next++ = ch;
    }

    if (unlikely(next != data->end())) // We had some non-length-preserving entity replacements.
        data->resize(next - data->begin());

    return not in_entity;
}

} // unnamed namespace

inline void SimpleXmlParser::skipWhiteSpace() {
    for (;;) {
        const int ch(input_->get());
        if (unlikely(ch == EOF))
            return;
        if (ch != ' ' and ch != '\t' and ch != '\n' and ch != '\r') {
            input_->putback(ch);
            return;
        } else if (ch == '\n')
            ++line_no_;
    }
}

inline bool SimpleXmlParser::extractName(std::string * const name) {
    name->clear();

    int ch(input_->get());
    if (unlikely(ch == EOF or (not StringUtil::IsAsciiLetter(ch) and ch != '_' and ch != ':'))) {
        input_->putback(ch);
        return false;
    }

    *name += static_cast<char>(ch);
    for (;;) {
        ch = input_->get();
        if (unlikely(ch == EOF))
            return false;
        if (not (StringUtil::IsAsciiLetter(ch) or StringUtil::IsDigit(ch) or ch == '_' or ch == ':' or ch == '.')) {
            input_->putback(ch);
            return true;
        }
        *name += static_cast<char>(ch);
    }
}

inline bool SimpleXmlParser::extractQuotedString(const int closing_quote, std::string * const s) {
    s->clear();

    for (;;) {
        const int ch(input_->get());
        if (unlikely(ch == EOF))
            return false;
        if (unlikely(ch == closing_quote))
            return true;
        *s += static_cast<char>(ch);
    }
}

bool SimpleXmlParser::getNext(Type * const type, std::map<std::string, std::string> * const attrib_map,
                              std::string * const data)
{
    if (unlikely(last_type_ == ERROR))
        throw std::runtime_error("in SimpleXmlParser::getNext: previous call already indicated an error!");

    attrib_map->clear();
    data->clear();

    if (last_element_was_empty_) {
        last_type_ = *type = CLOSING_TAG;
        data->swap(last_tag_name_);
        last_element_was_empty_ = false;
        last_type_ = CLOSING_TAG;
        return true;
    }

    int ch;
    if (last_type_ == OPENING_TAG) {
        last_type_ = *type = CHARACTERS;

        while ((ch = input_->get()) != '<') {
            if (unlikely(ch == EOF)) {
                last_error_message_ = "Unexpected EOF while looking for the start of a closing tag!";
                return false;
            }
            if (unlikely(ch == '\n'))
                ++line_no_;
            *data += static_cast<char>(ch);
        }
        input_->putback(ch); // Putting back the '<'.

        if (not DecodeEntities(data)) {
            last_type_ = *type = ERROR;
            last_error_message_ = "Invalid entity in character data ending on line " + std::to_string(line_no_) + "!";
            return false;
        }
    } else { // end-of-document or opening or closing tag
        skipWhiteSpace();

        ch = input_->get();
        if (unlikely(ch == EOF)) {
            last_type_ = *type = END_OF_DOCUMENT;
            return true;
        }

        if (ch != '<') {
            last_type_ = *type = ERROR;
            last_error_message_ = "Expected '<' on line " + std::to_string(line_no_) + ", found '"
                                  + std::string(1, static_cast<char>(ch)) + "' instead!";
            return false;
        }

        // If we're at the beginning, we may have an XML prolog:
        if (unlikely(last_type_ == UNINITIALISED) and input_->peek() == '?') {
            if (not parseProlog()) {
                last_type_ = *type = ERROR;
                return false;
            }
            last_type_ = *type = START_OF_DOCUMENT;
            return true;
        }

        ch = input_->get();
        if (ch == '/') { // A closing tag.
            if (unlikely(not parseClosingTag(data))) {
                last_type_ = *type = ERROR;
                last_error_message_ = "Error while parsing a closing tag on line " + std::to_string(line_no_) + "!";
                return false;
            }

            last_type_ = *type = CLOSING_TAG;
        } else { // An opening tag.
            input_->putback(ch);

            std::string error_message;
            if (unlikely(not parseOpeningTag(data, attrib_map, &error_message))) {
                last_type_ = *type = ERROR;
                last_error_message_ = "Error while parsing an opening tag on line " + std::to_string(line_no_) + "! ("
                                      + error_message + ")";
                return false;
            }

            ch = input_->get();
            if (ch == '/') {
                last_element_was_empty_ = true;
                last_tag_name_ = *data;
                ch = input_->get();
            }

            if (unlikely(ch != '>')) {
                last_type_ = *type = ERROR;
                last_error_message_ = "Error while parsing a opening tag on line " + std::to_string(line_no_) + "! ("
                                      "Closing angle bracket not found.)";
                return false;
            }

            last_type_ = *type = OPENING_TAG;
        }
    }

    return true;
}

bool SimpleXmlParser::parseProlog() {
    if (input_->peek() != '?')
        return true;
    input_->get();

    std::string prolog_tag_name;
    std::map<std::string, std::string> prolog_attrib_map;
    std::string error_message;
    if (not parseOpeningTag(&prolog_tag_name, &prolog_attrib_map, &error_message)) {
        last_error_message_ = "Error in prolog! (" + error_message + ")";
        return false;
    }

    int ch(input_->get());
    if (unlikely(ch != '?')) {
        last_error_message_ = "Error in prolog, expected '?' but found '" + std::string(1, static_cast<char>(ch)) + "'!";
        return false;
    }

    ch = input_->get();
    if (unlikely(ch != '>')) {
        last_error_message_ = "Error in prolog, closing angle bracket not found!";
        return false;
    }

    const auto encoding(prolog_attrib_map.find("encoding"));
    if (encoding != prolog_attrib_map.cend()) {
        if (::strcasecmp(encoding->second.c_str(), "utf-8") != 0) {
            last_error_message_ = "Error in prolog: We only support the UTF-8 encoding!";
            return false;
        }
    }

    return true;
}

bool SimpleXmlParser::parseOpeningTag(std::string * const tag_name,
                                      std::map<std::string, std::string> * const attrib_map,
                                      std::string * const error_message)
{
    attrib_map->clear();
    error_message->clear();

    if (unlikely(not extractName(tag_name))) {
        *error_message = "Failed to extract the tag name.";
        return false;
    }
    skipWhiteSpace();

    std::string attrib_name;
    while (extractName(&attrib_name)) {
        if (unlikely(attrib_map->find(attrib_name) != attrib_map->cend())) { // Duplicate attribute name?
            *error_message = "Found a duplicate tag name.";
            return false;
        }

        skipWhiteSpace();
        const int ch(input_->get());
        if (unlikely(ch != '=')) {
            *error_message = "Could not find an equal sign as part of an attribute.";
            return false;
        }

        skipWhiteSpace();
        const int quote(input_->get());
        if (unlikely(quote != '"' and quote != '\'')) {
            *error_message = "Found neither a single- nor a double-quote starting an attribute value.";
            return false;
        }
        std::string attrib_value;
        if (unlikely(not extractQuotedString(quote, &attrib_value))) {
            *error_message = "Failed to extract the attribute value.";
            return false;
        }

        (*attrib_map)[attrib_name] = attrib_value;

        skipWhiteSpace();
    }

    return true;
}

bool SimpleXmlParser::parseClosingTag(std::string * const tag_name) {
    tag_name->clear();

    if (not extractName(tag_name))
        return false;

    skipWhiteSpace();
    return input_->get() == '>';
}

std::string SimpleXmlParser::TypeToString(const Type type) {
    switch (type) {
    case UNINITIALISED:     return "UNINITIALISED";
    case START_OF_DOCUMENT: return "START_OF_DOCUMENT";
    case END_OF_DOCUMENT:   return "END_OF_DOCUMENT";
    case ERROR:             return "ERROR";
    case OPENING_TAG:       return "OPENING_TAG";
    case CLOSING_TAG:       return "CLOSING_TAG";
    case CHARACTERS:        return "CHARACTERS";
    }

    __builtin_unreachable();
}
<commit_msg>Fixed a typo.<commit_after>/** \file   SimpleXmlParser.cc
 *  \brief  A non-validating XML parser class.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "SimpleXmlParser.h"
#include <stdexcept>
#include "Compiler.h"
#include "StringUtil.h"
#include "TextUtil.h"

namespace {

inline bool DecodeEnity(const std::string &entity_string, std::string * const decoded_char) {
    if (unlikely(entity_string.empty()))
        return false;

    if (__builtin_strcmp(entity_string.c_str(), "amp") == 0)
        *decoded_char = "&";
    else if (__builtin_strcmp(entity_string.c_str(), "apos") == 0)
        *decoded_char = "'";
    else if (__builtin_strcmp(entity_string.c_str(), "quot") == 0)
        *decoded_char = "\"";
    else if (__builtin_strcmp(entity_string.c_str(), "lt") == 0)
        *decoded_char = "<";
    else if (__builtin_strcmp(entity_string.c_str(), "gt") == 0)
        *decoded_char = ">";
    else if (unlikely(entity_string[0] == '#')) {
        if (entity_string.length() < 2)
            return false;

        unsigned code_point;
        if (entity_string[1] == 'x') {
            if (entity_string.length() < 3 or entity_string.length() > 6)
                return false;
            if (not StringUtil::ToUnsigned(entity_string.substr(2), &code_point, 16))
                return false;
        } else {
            if (entity_string.length() < 2 or entity_string.length() > 6)
                return false;
            if (not StringUtil::ToUnsigned(entity_string.substr(1), &code_point))
                return false;
        }

        if (not TextUtil::WCharToUTF8String(static_cast<wchar_t>(code_point), decoded_char))
            return false;
    } else
        return false;

    return true;
}

// Replaces entities in-place and resizes "data", if necessary.
inline bool DecodeEntities(std::string * const data) {
    if (unlikely(data->empty()))
        return true;

    std::string::iterator next(data->begin());
    bool in_entity(false);
    std::string entity;
    for (const auto ch : *data) {
        if (unlikely(in_entity)) {
            if (ch == ';') {
                std::string decoded_char;
                if (not DecodeEnity(entity, &decoded_char))
                    return false;
                for (const auto dch : decoded_char)
                    *next++ = dch;
                in_entity = false;
            } else
                entity += ch;
        } else if (unlikely(ch == '&')) {
            in_entity = true;
            entity.clear();
        } else
            *next++ = ch;
    }

    if (unlikely(next != data->end())) // We had some non-length-preserving entity replacements.
        data->resize(next - data->begin());

    return not in_entity;
}

} // unnamed namespace

inline void SimpleXmlParser::skipWhiteSpace() {
    for (;;) {
        const int ch(input_->get());
        if (unlikely(ch == EOF))
            return;
        if (ch != ' ' and ch != '\t' and ch != '\n' and ch != '\r') {
            input_->putback(ch);
            return;
        } else if (ch == '\n')
            ++line_no_;
    }
}

inline bool SimpleXmlParser::extractName(std::string * const name) {
    name->clear();

    int ch(input_->get());
    if (unlikely(ch == EOF or (not StringUtil::IsAsciiLetter(ch) and ch != '_' and ch != ':'))) {
        input_->putback(ch);
        return false;
    }

    *name += static_cast<char>(ch);
    for (;;) {
        ch = input_->get();
        if (unlikely(ch == EOF))
            return false;
        if (not (StringUtil::IsAsciiLetter(ch) or StringUtil::IsDigit(ch) or ch == '_' or ch == ':' or ch == '.')) {
            input_->putback(ch);
            return true;
        }
        *name += static_cast<char>(ch);
    }
}

inline bool SimpleXmlParser::extractQuotedString(const int closing_quote, std::string * const s) {
    s->clear();

    for (;;) {
        const int ch(input_->get());
        if (unlikely(ch == EOF))
            return false;
        if (unlikely(ch == closing_quote))
            return true;
        *s += static_cast<char>(ch);
    }
}

bool SimpleXmlParser::getNext(Type * const type, std::map<std::string, std::string> * const attrib_map,
                              std::string * const data)
{
    if (unlikely(last_type_ == ERROR))
        throw std::runtime_error("in SimpleXmlParser::getNext: previous call already indicated an error!");

    attrib_map->clear();
    data->clear();

    if (last_element_was_empty_) {
        last_type_ = *type = CLOSING_TAG;
        data->swap(last_tag_name_);
        last_element_was_empty_ = false;
        last_type_ = CLOSING_TAG;
        return true;
    }

    int ch;
    if (last_type_ == OPENING_TAG) {
        last_type_ = *type = CHARACTERS;

        while ((ch = input_->get()) != '<') {
            if (unlikely(ch == EOF)) {
                last_error_message_ = "Unexpected EOF while looking for the start of a closing tag!";
                return false;
            }
            if (unlikely(ch == '\n'))
                ++line_no_;
            *data += static_cast<char>(ch);
        }
        input_->putback(ch); // Putting back the '<'.

        if (not DecodeEntities(data)) {
            last_type_ = *type = ERROR;
            last_error_message_ = "Invalid entity in character data ending on line " + std::to_string(line_no_) + "!";
            return false;
        }
    } else { // end-of-document or opening or closing tag
        skipWhiteSpace();

        ch = input_->get();
        if (unlikely(ch == EOF)) {
            last_type_ = *type = END_OF_DOCUMENT;
            return true;
        }

        if (ch != '<') {
            last_type_ = *type = ERROR;
            last_error_message_ = "Expected '<' on line " + std::to_string(line_no_) + ", found '"
                                  + std::string(1, static_cast<char>(ch)) + "' instead!";
            return false;
        }

        // If we're at the beginning, we may have an XML prolog:
        if (unlikely(last_type_ == UNINITIALISED) and input_->peek() == '?') {
            if (not parseProlog()) {
                last_type_ = *type = ERROR;
                return false;
            }
            last_type_ = *type = START_OF_DOCUMENT;
            return true;
        }

        ch = input_->get();
        if (ch == '/') { // A closing tag.
            if (unlikely(not parseClosingTag(data))) {
                last_type_ = *type = ERROR;
                last_error_message_ = "Error while parsing a closing tag on line " + std::to_string(line_no_) + "!";
                return false;
            }

            last_type_ = *type = CLOSING_TAG;
        } else { // An opening tag.
            input_->putback(ch);

            std::string error_message;
            if (unlikely(not parseOpeningTag(data, attrib_map, &error_message))) {
                last_type_ = *type = ERROR;
                last_error_message_ = "Error while parsing an opening tag on line " + std::to_string(line_no_) + "! ("
                                      + error_message + ")";
                return false;
            }

            ch = input_->get();
            if (ch == '/') {
                last_element_was_empty_ = true;
                last_tag_name_ = *data;
                ch = input_->get();
            }

            if (unlikely(ch != '>')) {
                last_type_ = *type = ERROR;
                last_error_message_ = "Error while parsing an opening tag on line " + std::to_string(line_no_) + "! ("
                                      "Closing angle bracket not found.)";
                return false;
            }

            last_type_ = *type = OPENING_TAG;
        }
    }

    return true;
}

bool SimpleXmlParser::parseProlog() {
    if (input_->peek() != '?')
        return true;
    input_->get();

    std::string prolog_tag_name;
    std::map<std::string, std::string> prolog_attrib_map;
    std::string error_message;
    if (not parseOpeningTag(&prolog_tag_name, &prolog_attrib_map, &error_message)) {
        last_error_message_ = "Error in prolog! (" + error_message + ")";
        return false;
    }

    int ch(input_->get());
    if (unlikely(ch != '?')) {
        last_error_message_ = "Error in prolog, expected '?' but found '" + std::string(1, static_cast<char>(ch)) + "'!";
        return false;
    }

    ch = input_->get();
    if (unlikely(ch != '>')) {
        last_error_message_ = "Error in prolog, closing angle bracket not found!";
        return false;
    }

    const auto encoding(prolog_attrib_map.find("encoding"));
    if (encoding != prolog_attrib_map.cend()) {
        if (::strcasecmp(encoding->second.c_str(), "utf-8") != 0) {
            last_error_message_ = "Error in prolog: We only support the UTF-8 encoding!";
            return false;
        }
    }

    return true;
}

bool SimpleXmlParser::parseOpeningTag(std::string * const tag_name,
                                      std::map<std::string, std::string> * const attrib_map,
                                      std::string * const error_message)
{
    attrib_map->clear();
    error_message->clear();

    if (unlikely(not extractName(tag_name))) {
        *error_message = "Failed to extract the tag name.";
        return false;
    }
    skipWhiteSpace();

    std::string attrib_name;
    while (extractName(&attrib_name)) {
        if (unlikely(attrib_map->find(attrib_name) != attrib_map->cend())) { // Duplicate attribute name?
            *error_message = "Found a duplicate tag name.";
            return false;
        }

        skipWhiteSpace();
        const int ch(input_->get());
        if (unlikely(ch != '=')) {
            *error_message = "Could not find an equal sign as part of an attribute.";
            return false;
        }

        skipWhiteSpace();
        const int quote(input_->get());
        if (unlikely(quote != '"' and quote != '\'')) {
            *error_message = "Found neither a single- nor a double-quote starting an attribute value.";
            return false;
        }
        std::string attrib_value;
        if (unlikely(not extractQuotedString(quote, &attrib_value))) {
            *error_message = "Failed to extract the attribute value.";
            return false;
        }

        (*attrib_map)[attrib_name] = attrib_value;

        skipWhiteSpace();
    }

    return true;
}

bool SimpleXmlParser::parseClosingTag(std::string * const tag_name) {
    tag_name->clear();

    if (not extractName(tag_name))
        return false;

    skipWhiteSpace();
    return input_->get() == '>';
}

std::string SimpleXmlParser::TypeToString(const Type type) {
    switch (type) {
    case UNINITIALISED:     return "UNINITIALISED";
    case START_OF_DOCUMENT: return "START_OF_DOCUMENT";
    case END_OF_DOCUMENT:   return "END_OF_DOCUMENT";
    case ERROR:             return "ERROR";
    case OPENING_TAG:       return "OPENING_TAG";
    case CLOSING_TAG:       return "CLOSING_TAG";
    case CHARACTERS:        return "CHARACTERS";
    }

    __builtin_unreachable();
}
<|endoftext|>