/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2019                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#define _XOPEN_SOURCE 700

#include <stdint.h>
#include <string.h>

#include "sion_debug.h"

void sion_swap(void *target, void *source, int size, int n, int do_swap)
{
  int i, j;
  char *iptr, *optr;

  optr = target;
  iptr = source;

  if (iptr == optr) {
    if (!do_swap) {
      return;
    }
    /*
     * in-place swapping
     * unroll loop for the common cases 2,4,8 Byte
     *
     */
    DPRINTF((4, "sion_swap: swapping %d x %d Bytes in place =%ld\n", n, size, (int32_t)*iptr));

    char tmp;
    switch (size) {
    case 1:
      /* nothing to do */
      break;
    case 2:
      for (i = 0; i < n; i++) {
        tmp = iptr[0];
        iptr[0] = iptr[1];
        iptr[1] = tmp;
        iptr += 2;
      }
      break;
    case 4:
      for (i = 0; i < n; i++) {
        tmp = iptr[0];
        iptr[0] = iptr[3];
        iptr[3] = tmp;

        tmp = iptr[1];
        iptr[1] = iptr[2];
        iptr[2] = tmp;

        iptr += 4;
      }
      break;
    case 8:
      for (i = 0; i < n; i++) {
        tmp = iptr[0];
        iptr[0] = iptr[7];
        iptr[7] = tmp;

        tmp = iptr[1];
        iptr[1] = iptr[6];
        iptr[6] = tmp;

        tmp = iptr[2];
        iptr[2] = iptr[5];
        iptr[5] = tmp;

        tmp = iptr[3];
        iptr[3] = iptr[4];
        iptr[4] = tmp;

        iptr += 8;
      }
      break;
    default:
      for (i = 0; i < n; i++) {
        for (j = 0; j < size / 2; j++) {
          tmp = iptr[j];
          iptr[j] = iptr[size - j - 1];
          iptr[size - j - 1] = tmp;
        }
        iptr += size;
      }
    }
    DPRINTF((4, "sion_swap: swapped   %d x %d Bytes in place =%ld\n", n, size, *optr));

  } else {
    if (!do_swap) {
      memcpy(optr, iptr, size * n);
      return;
    }
    /*
     * swap into a different non-overlapping buffer
     * unroll common cases again
     */
    DPRINTF((4, "sion_swap: swapping %d x %d Bytes\n", n, size));

    switch (size) {
    case 1:
      memcpy(optr, iptr, size * n);
      break;
    case 2:
      for (i = 0; i < n; i++) {
        optr[0] = iptr[1];
        optr[1] = iptr[0];

        iptr += 2;
        optr += 2;
      }
      break;
    case 4:
      for (i = 0; i < n; i++) {
        optr[0] = iptr[3];
        optr[1] = iptr[2];
        optr[2] = iptr[1];
        optr[3] = iptr[0];

        iptr += 4;
        optr += 4;
      }
      break;
    case 8:
      for (i = 0; i < n; i++) {
        optr[0] = iptr[7];
        optr[1] = iptr[6];
        optr[2] = iptr[5];
        optr[3] = iptr[4];
        optr[4] = iptr[3];
        optr[5] = iptr[2];
        optr[6] = iptr[1];
        optr[7] = iptr[0];

        iptr += 8;
        optr += 8;
      }
      break;
    default:
      for (i = 0; i < n; i++) {
        for (j = 0; j < size; j++) {
          optr[j] = iptr[size - j - 1];
        }
        iptr += size;
        optr += size;
      }
    }
  }
}
