//===- ELFSegment.h -------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_ELF_SEGMENT_H
#define MCLD_ELF_SEGMENT_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif
#include <llvm/Support/ELF.h>
#include <llvm/Support/DataTypes.h>
#include <mcld/LD/LDSection.h>
#include <cassert>
#include <vector>

namespace mcld
{

/** \class ELFSegment
 *  \brief decribe the program header for ELF executable or shared object
 */
class ELFSegment
{
public:
  typedef std::vector<LDSection*>::iterator sect_iterator;
  typedef std::vector<LDSection*>::const_iterator const_sect_iterator;
public:
  ELFSegment(uint32_t pType,
             uint32_t pFlag = llvm::ELF::PF_R,
             uint64_t pOffset = 0,
             uint64_t pVaddr = 0,
             uint64_t pPaddr = 0,
             uint64_t pFilesz = 0,
             uint64_t pMemsz = 0,
             uint64_t pAlign = 0,
             uint64_t pMaxSectAlign = 0);
  ~ELFSegment();

  ///  -----  iterators  -----  ///
  sect_iterator       begin()       { return m_SectionList.begin(); }
  const_sect_iterator begin() const { return m_SectionList.begin(); }
  sect_iterator       end()         { return m_SectionList.end(); }
  const_sect_iterator end()   const { return m_SectionList.end(); }

  LDSection*       front()       { return m_SectionList.front(); }
  const LDSection* front() const { return m_SectionList.front(); }
  LDSection*       back()        { return m_SectionList.back(); }
  const LDSection* back()  const { return m_SectionList.back(); }

  ///  -----  observers  -----  ///
  uint32_t type() const
  { return m_Type; }

  uint64_t offset() const
  { return m_Offset; }

  uint64_t vaddr() const
  { return m_Vaddr; }

  uint64_t paddr() const
  { return m_Paddr; }

  uint64_t filesz() const
  { return m_Filesz; }

  uint64_t memsz() const
  { return m_Memsz; }

  uint32_t flag() const
  { return m_Flag; }

  uint64_t align() const
  { return std::max(m_Align, m_MaxSectionAlign); }

  size_t numOfSections() const
  { return m_SectionList.size(); }

  bool isDataSegment() const;

  bool isBssSegment() const;

  ///  -----  modifiers  -----  ///
  void setOffset(uint64_t pOffset)
  { m_Offset = pOffset; }

  void setVaddr(uint64_t pVaddr)
  { m_Vaddr = pVaddr; }

  void setPaddr(uint64_t pPaddr)
  { m_Paddr = pPaddr; }

  void setFilesz(uint64_t pFilesz)
  { m_Filesz = pFilesz; }

  void setMemsz(uint64_t pMemsz)
  { m_Memsz = pMemsz; }

  void setFlag(uint32_t pFlag)
  { m_Flag = pFlag; }

  void updateFlag(uint32_t pFlag)
  {
    // PT_TLS segment should be PF_R
    if (llvm::ELF::PT_TLS != m_Type)
      m_Flag |= pFlag;
  }

  void setAlign(uint64_t pAlign)
  { m_Align = pAlign; }

  void addSection(LDSection* pSection)
  {
    assert(NULL != pSection);
    if (pSection->align() > m_MaxSectionAlign)
      m_MaxSectionAlign = pSection->align();
    m_SectionList.push_back(pSection);
  }

private:
  uint32_t m_Type;            // Type of segment
  uint32_t m_Flag;            // Segment flags
  uint64_t m_Offset;          // File offset where segment is located, in bytes
  uint64_t m_Vaddr;           // Virtual address of the segment
  uint64_t m_Paddr;           // Physical address of the segment (OS-specific)
  uint64_t m_Filesz;          // # of bytes in file image of segment (may be 0)
  uint64_t m_Memsz;           // # of bytes in mem image of segment (may be 0)
  uint64_t m_Align;           // alignment constraint
  uint64_t m_MaxSectionAlign; // max alignment of the sections in this segment
  std::vector<LDSection*> m_SectionList;
};

} // namespace of mcld

#endif