//===-- DWARFDebugPubnamesSet.cpp -------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "DWARFDebugPubnamesSet.h"

#include "lldb/Core/Log.h"
#include "lldb/Core/RegularExpression.h"

#include "SymbolFileDWARF.h"

using namespace lldb_private;

DWARFDebugPubnamesSet::DWARFDebugPubnamesSet()
    : m_offset(DW_INVALID_OFFSET), m_header(), m_descriptors(),
      m_name_to_descriptor_index() {}

DWARFDebugPubnamesSet::DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset,
                                             dw_offset_t cu_die_offset,
                                             dw_offset_t cu_die_length)
    : m_offset(debug_aranges_offset), m_header(), m_descriptors(),
      m_name_to_descriptor_index() {
  m_header.length =
      10; // set the length to only include the header right for now
  m_header.version = 2;                // The DWARF version number
  m_header.die_offset = cu_die_offset; // compile unit .debug_info offset
  m_header.die_length = cu_die_length; // compile unit .debug_info length
}

void DWARFDebugPubnamesSet::AddDescriptor(dw_offset_t cu_rel_offset,
                                          const char *name) {
  if (name && name[0]) {
    // Adjust our header length
    m_header.length += strlen(name) + 1 + sizeof(dw_offset_t);
    Descriptor pubnameDesc(cu_rel_offset, name);
    m_descriptors.push_back(pubnameDesc);
  }
}

void DWARFDebugPubnamesSet::Clear() {
  m_offset = DW_INVALID_OFFSET;
  m_header.length = 10;
  m_header.version = 2;
  m_header.die_offset = DW_INVALID_OFFSET;
  m_header.die_length = 0;
  m_descriptors.clear();
}

//----------------------------------------------------------------------
// InitNameIndexes
//----------------------------------------------------------------------
void DWARFDebugPubnamesSet::InitNameIndexes() const {
  // Create the name index vector to be able to quickly search by name
  const size_t count = m_descriptors.size();
  for (uint32_t idx = 0; idx < count; ++idx) {
    const char *name = m_descriptors[idx].name.c_str();
    if (name && name[0])
      m_name_to_descriptor_index.insert(
          cstr_to_index_mmap::value_type(name, idx));
  }
}

bool DWARFDebugPubnamesSet::Extract(const DWARFDataExtractor &data,
                                    lldb::offset_t *offset_ptr) {
  if (data.ValidOffset(*offset_ptr)) {
    m_descriptors.clear();
    m_offset = *offset_ptr;
    m_header.length = data.GetDWARFInitialLength(offset_ptr);
    m_header.version = data.GetU16(offset_ptr);
    m_header.die_offset = data.GetDWARFOffset(offset_ptr);
    m_header.die_length = data.GetDWARFOffset(offset_ptr);

    Descriptor pubnameDesc;
    while (data.ValidOffset(*offset_ptr)) {
      pubnameDesc.offset = data.GetDWARFOffset(offset_ptr);

      if (pubnameDesc.offset) {
        const char *name = data.GetCStr(offset_ptr);
        if (name && name[0]) {
          pubnameDesc.name = name;
          m_descriptors.push_back(pubnameDesc);
        }
      } else
        break; // We are done if we get a zero 4 byte offset
    }

    return !m_descriptors.empty();
  }
  return false;
}

dw_offset_t DWARFDebugPubnamesSet::GetOffsetOfNextEntry() const {
  return m_offset + m_header.length + 4;
}

void DWARFDebugPubnamesSet::Dump(Log *log) const {
  log->Printf("Pubnames Header: length = 0x%8.8x, version = 0x%4.4x, "
              "die_offset = 0x%8.8x, die_length = 0x%8.8x",
              m_header.length, m_header.version, m_header.die_offset,
              m_header.die_length);

  bool verbose = log->GetVerbose();

  DescriptorConstIter pos;
  DescriptorConstIter end = m_descriptors.end();
  for (pos = m_descriptors.begin(); pos != end; ++pos) {
    if (verbose)
      log->Printf("0x%8.8x + 0x%8.8x = 0x%8.8x: %s", pos->offset,
                  m_header.die_offset, pos->offset + m_header.die_offset,
                  pos->name.c_str());
    else
      log->Printf("0x%8.8x: %s", pos->offset + m_header.die_offset,
                  pos->name.c_str());
  }
}

void DWARFDebugPubnamesSet::Find(
    const char *name, bool ignore_case,
    std::vector<dw_offset_t> &die_offset_coll) const {
  if (!m_descriptors.empty() && m_name_to_descriptor_index.empty())
    InitNameIndexes();

  std::pair<cstr_to_index_mmap::const_iterator,
            cstr_to_index_mmap::const_iterator>
      range(m_name_to_descriptor_index.equal_range(name));
  for (cstr_to_index_mmap::const_iterator pos = range.first;
       pos != range.second; ++pos)
    die_offset_coll.push_back(m_header.die_offset +
                              m_descriptors[(*pos).second].offset);
}

void DWARFDebugPubnamesSet::Find(
    const RegularExpression &regex,
    std::vector<dw_offset_t> &die_offset_coll) const {
  DescriptorConstIter pos;
  DescriptorConstIter end = m_descriptors.end();
  for (pos = m_descriptors.begin(); pos != end; ++pos) {
    if (regex.Execute(pos->name))
      die_offset_coll.push_back(m_header.die_offset + pos->offset);
  }
}
