/* Copyright (C) 2007-2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** 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 General Public License for more details.
*/

/*
 * Contains some helpful macros, and inline routines.
 */

#ifndef ELFF_ELF_DEFS_H_
#define ELFF_ELF_DEFS_H_

#include "elf.h"

//=============================================================================
// Macros.
//=============================================================================

/* Increments a pointer by n bytes.
 * Param:
 *  p - Pointer to increment.
 *  n - Number of bytes to increment the pointer with.
 */
#define INC_PTR(p, n)   (reinterpret_cast<uint8_t*>(p) + (n))

/* Increments a constant pointer by n bytes.
 * Param:
 *  p - Pointer to increment.
 *  n - Number of bytes to increment the pointer with.
 */
#define INC_CPTR(p, n)  (reinterpret_cast<const uint8_t*>(p) + (n))

/* Increments a pointer of a given type by n bytes.
 * Param:
 *  T - Pointer type
 *  p - Pointer to increment.
 *  n - Number of bytes to increment the pointer with.
 */
#define INC_PTR_T(T, p, n)                              \
    reinterpret_cast<T*>                                \
        (reinterpret_cast<uint8_t*>(p) + (n))

/* Increments a constant pointer of a given type by n bytes.
 * Param:
 *  T - Pointer type
 *  p - Pointer to increment.
 *  n - Number of bytes to increment the pointer with.
 */
#define INC_CPTR_T(T, p, n)                                 \
    reinterpret_cast<const T*>                              \
        (reinterpret_cast<const uint8_t*>(p) + (n))

/* Calculates number of entries in a static array.
 * Param:
 *  a - Array.
 * Return:
 *  Number of entries in the array.
 */
#define ELFF_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))

/* Calculates offset of a field inside a structure (or a class) of the
 * given type.
 * Param:
 *  T - Structure (or class) type.
 *  f - Name of a field (member variable) for this structure (or class).
 */
#define ELFF_FIELD_OFFSET(T, f) ((size_t)(size_t*)&(((T *)0)->f))

//=============================================================================
// Inline routines.
//=============================================================================

/* Calculates byte interval between two pointers.
 * Param:
 *  s - Starting pointer of the interval. Must be less, or equal to 'e'.
 *  e - Ending pointer of the interval. Must be greater, or equal to 's'.
 * Return:
 *  Byte interval between two pointers.
 */
static inline size_t
diff_ptr(const void* s, const void* e) {
  assert(s <= e);
  return ((size_t)(reinterpret_cast<const uint8_t*>(e) -
         reinterpret_cast<const uint8_t*>(s)));
}

/* Gets one byte from an index inside a memory block.
 * Param:
 *  ptr - Address of the beginning of the memory block.
 *  bt - Index of a byte inside the block to get.
 * Return:
 *  A byte at the given index inside the given memory block.
 */
static inline uint8_t
get_byte(const void* ptr, uint32_t bt) {
  return *(reinterpret_cast<const uint8_t*>(ptr) + bt);
}

/* Checks if given address range is fully contained within a section.
 * Param:
 *  rp - Beginning of the range to check.
 *  rsize - Size of the range to check.
 *  ss - Beginning of the section that should contain the checking range.
 *  ssize - Size of the section that should contain the checking range.
 * Return:
 *  true, if given address range is fully contained within a section, or
 *  false, if any part of the address range is not contained in the secton.
 */
static inline bool
is_in_section(const void* rp, size_t rsize, const void* ss, size_t ssize) {
  const void* rend = INC_CPTR(rp, rsize);
  /* We also make sure here that increment didn't overflow the pointer. */
  return rp >= ss && ss != NULL && (diff_ptr(ss, rend) <= ssize) && rend >= rp;
}

/* Checks if this code runs on CPU with a little-endian data format.
 * Return:
 *  true, if this code runs on CPU with a little-endian data format,
 *  or false, if this code runs on CPU with a big-endian data format.
 */
static inline bool
is_little_endian_cpu(void) {
  uint16_t tmp = 0x00FF;
  /* Lets see if byte has flipped for little-endian. */
  return get_byte(&tmp, 0) == 0xFF;
}

#endif  // ELFF_ELF_DEFS_H_
