//===- ELFAttributeData.cpp -----------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "mcld/Target/ELFAttributeData.h"

#include "mcld/Support/LEB128.h"
#include "mcld/Target/ELFAttributeValue.h"
#include <cstring>
#include <cassert>

namespace mcld {

bool ELFAttributeData::ReadTag(TagType& pTag,
                               const char*& pBuf,
                               size_t& pBufSize) {
  size_t size = 0;

  pTag = static_cast<ELFAttributeData::TagType>(
      leb128::decode<uint64_t>(pBuf, size));

  if (size > pBufSize)
    return false;

  pBuf += size;
  pBufSize -= size;

  return true;
}

bool ELFAttributeData::ReadValue(ELFAttributeValue& pValue,
                                 const char*& pBuf,
                                 size_t& pBufSize) {
  // An ULEB128-encoded value
  if (pValue.isIntValue()) {
    size_t size = 0;
    uint64_t int_value = leb128::decode<uint64_t>(pBuf, size);
    pValue.setIntValue(static_cast<unsigned int>(int_value));

    if (size > pBufSize)
      return false;

    pBuf += size;
    pBufSize -= size;
  }

  // A null-terminated byte string
  if (pValue.isStringValue()) {
    pValue.setStringValue(pBuf);

    size_t size = pValue.getStringValue().length() + 1 /* '\0' */;
    assert(size <= pBufSize);
    pBuf += size;
    pBufSize -= size;
  }

  return true;
}

bool ELFAttributeData::WriteAttribute(TagType pTag,
                                      const ELFAttributeValue& pValue,
                                      char*& pBuf) {
  // Write the attribute tag.
  leb128::encode<uint32_t>(pBuf, pTag);

  // Write the attribute value.
  if (pValue.isIntValue())
    leb128::encode<uint32_t>(pBuf, pValue.getIntValue());

  if (pValue.isStringValue()) {
    // Write string data.
    size_t str_val_len = pValue.getStringValue().length();

    if (str_val_len > 0)
      ::memcpy(pBuf, pValue.getStringValue().c_str(), str_val_len);
    pBuf += str_val_len;

    // Write NULL-terminator.
    *pBuf++ = '\0';
  }

  return true;
}

}  // namespace mcld