# /*
#              Testfile for alignment of values
#
#               Joerg Schoen 1996-2009 <first-name AT die-Schoens DOT de>
#
#   Check imposed and required alignments of data types:
#   1. "Imposed" means alignment used in struct's. Are there any padding
#      bytes inserted in structure definitions to force alignment of types
#      on multiples of a certain alignment value? If so, it prints
#        -DALGN_TYPE=<alignment>
#   2. "Required" means necessary to avoid bus errors. A variable can only
#      be accessed at multiples of an alignment value, if not, usually
#      a bus error results. If this is detected, the program prints
#        -DNEED_ALGN_TYPE=<alignment>
#
#   The basic data types short, int, long, long long, double, and long double
#    are tested.
#
#   The following table shows the results for some platforms I have access to
#    (values are imposed/required. If none is given for required, the machine
#    can access values at any address):
#
#============================================================================
#                 |             |        |        |        |        |  LONG  |
#  System         |   Compiler  | SHORT  |  INT   |  LONG  | DOUBLE | DOUBLE |
#                 |             |        |        |        |        |        |
#=============================================================================
#  GNU 4.3.2      | gcc         |  2     |  4     |  4     |   4    |  4     |
#                 | gcc -O3     |  2/2   |  4/4   |  8/8   |   8/8  |  4     |
#-----------------------------------------------------------------------------
#
if [ -z "$CC" ]
then
  CC=cc
fi

opts=
while :
do
  case "$1" in
    -D*)
      opts="$opts $1"
    ;;
    *)
      break
    ;;
  esac
  shift
done

if $CC -DHAS_STDINT -DHAS_LONGLONG $opts $0
then
  for i in short int long long_long double long_double
  do
    ./a.out $i
  done
  rm a.out
else
  echo "Failed to compile!"
fi

exit
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <setjmp.h>
#include <signal.h>
#include <errno.h>

int testalign(int type); /*  prototype  */

int main(int argc, char *argv[])
{
  int ret, what;

  ret = 0;

  if(argc != 2)
    exit(5);

  if(strcmp(argv[1], "short") == 0)
    what = 1;
  else if(strcmp(argv[1], "int") == 0)
    what = 2;
  else if(strcmp(argv[1], "long") == 0)
    what = 3;
  else if(strcmp(argv[1], "long_long") == 0)
    what = 4;
  else if(strcmp(argv[1], "double") == 0)
    what = 5;
  else if(strcmp(argv[1], "long_double") == 0)
    what = 6;
  else
    exit(10);

  /*  Check for required alignments  */
  if(testalign(what) == -1)
    ret = 20;

  return ret;
}

/*  The signal we expect when accessing bad aligned memory  */
#ifndef EXPECTED_SIGNAL1
# define EXPECTED_SIGNAL1   SIGBUS
# define EXPECTED_SIGNAL2   SIGSEGV
#endif

static void testalignsig(int sig);

typedef long long long_long;
typedef long double long_double;

#define GEN_FILLARRAY(type)			\
  void fillarr_ ## type(type *array, int n)	\
  {						\
    int i;					\
						\
    for(i = 0 ; i < n ; ++i)			\
      array[i] = (type)-1;			\
  }

GEN_FILLARRAY(short)
GEN_FILLARRAY(int)
GEN_FILLARRAY(long)
GEN_FILLARRAY(long_long)
GEN_FILLARRAY(double)
GEN_FILLARRAY(long_double)

volatile int Align;
volatile const char *Name;

int testalign(int what)
{
  char *a;
  int len;

  /*  Assume long double is biggest type  */
  if((a = (char *)malloc(100 * sizeof(long double))) == NULL)
    goto error;

#define TESTALGN1(type, name)						\
  Name = name;								\
  {									\
    struct {								\
      char c;								\
      type var;								\
    } sTest;								\
    									\
    if(((char*)&(sTest.var) - (char*)&sTest) != sizeof(sTest.c))	\
      printf("-DALGN_%s=%d\n", name, (int)((char*)&(sTest.var) - (char*)&sTest)); \
  }									\
  len = sizeof(type)

  switch(what) {
  case 1:
    TESTALGN1(short, "SHORT");
    break;
  case 2:
    TESTALGN1(int, "INT");
    break;
  case 3:
    TESTALGN1(long, "LONG");
    break;
  case 4:
    TESTALGN1(long_long, "LONG");
    break;
  case 5:
    TESTALGN1(double, "DOUBLE");
    break;
  case 6:
    TESTALGN1(long_double, "LONGDOUBLE");
    break;
  default:
    goto error;
  }
#undef TESTALGN1

  /*  Set signal handler  */
  if(signal(EXPECTED_SIGNAL1, testalignsig) == SIG_ERR ||
     signal(EXPECTED_SIGNAL2, testalignsig) == SIG_ERR)
    goto error;

  Align = len;
  for(len >>= 1 ; len >= 1 ; len >>= 1) {
    /*printf("DO %d\n", len);*/

#define TESTALGN2(type, align)			\
    *(type *)&a[align] = 11; 			\
    fillarr_ ## type((type *)(a + align), 99)

    switch(what) {
    case 1: TESTALGN2(short, len);       break;
    case 2: TESTALGN2(int, len);         break;
    case 3: TESTALGN2(long, len);        break;
    case 4: TESTALGN2(long_long, len);   break;
    case 5: TESTALGN2(double, len);      break;
    case 6: TESTALGN2(long_double, len); break;
    }
#undef TESTALGN2

    /*  This value worked!  */
    Align = len;
  }

  free((void *)a);

  return 0;
error:
  fprintf(stderr, "WARNING: Error in testalign \"%s\"!\n", strerror(errno));
  return -1;
}

/*  The signal function that catches illegal memory accesses  */
static void testalignsig(int sig)
{
  printf("-DNEED_ALGN_%s=%d\n", Name, Align);
  exit(0);
}
