/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the LICENSE file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*-------------------------------------------------------------------------
 *
 * Created:     atomic_writer.c
 *
 * Purpose:     This is the "writer" part of the standalone test to check
 *              atomic read-write operation on a system.
 *                  a) atomic_writer.c--the writer (this file)
 *                  b) atomic_reader.c--the reader
 *                  c) atomic_data--the name of the data file used by writer and reader
 *
 *-------------------------------------------------------------------------
 */

/***********/
/* Headers */
/***********/

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#if !defined(WIN32) && !defined(__MINGW32__) && !defined(_WIN32)

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

/****************/
/* Local Macros */
/****************/

#define FILENAME "atomic_data"

/********************/
/* Local Prototypes */
/********************/

static void usage(void);

/*-------------------------------------------------------------------------
 * Function:    usage
 *
 * Purpose:     To print information about the command line options
 *
 * Parameters:  None
 *
 * Return:      void
 *
 *-------------------------------------------------------------------------
 */
static void
usage(void)
{
    printf("\n");
    printf("Usage error!\n");
    printf("Usage: atomic_writer -n <number of integers to write> -i <number of iterations for writer>\n");
    printf("       Note**The number of integers for option n has to be positive\n");
    printf("       Note**The number of integers for option i has to be positive\n");
    printf("\n");
} /* usage() */

/*-------------------------------------------------------------------------
 * Function:    main
 *
 * Purpose:     To write a series of integers to a file for the reader to verify the data.
 *              A write is atomic if the whole amount written in one operation is not interleaved
 *              with data from any other process.
 *              (1) Iterate with i iterations
 *              (2) Write a series of integers (0 to n-1) to the file with this pattern:
 *              offset 0:   0000000000000000000000000000000
 *              offset 1:   111111111111111111111111111111
 *              offset 2:   22222222222222222222222222222
 *              offset 3:   3333333333333333333333333333
 *                  ...
 *                  ...
 *              offset n-1: (n-1)
 *
 *              At the end of the writes, the data in the file will be:
 *              01234567........(n-1)
 *
 * Note:
 *      (a) The # of integers (via -n option) used by the writer and reader should be the same.
 *      (b) The data file used by the writer and reader should be the same.
 *
 * Future enhancement:
 *      1) Provide default values for n and i and allow user to run with either 0 or 1 option
 *      2) Use HDF library HD<system calls> instead of the system calls
 *      3) Handle large sized buffer (gigabytes) if needed
 *
 * Return:      Success:    EXIT_SUCCESS
 *              Failure:    EXIT_FAILURE
 *
 *-------------------------------------------------------------------------
 */
int
main(int argc, char *argv[])
{
    int           fd = -1;        /* file descriptor                      */
    ssize_t       bytes_wrote;    /* the number of bytes written          */
    unsigned int *buf = NULL;     /* buffer to hold written data          */
    unsigned int  n, u, i;        /* local index variable                 */
    int           temp;           /* temporary variable                   */
    unsigned int  iterations = 0; /* the input for "-i"                   */
    unsigned int  num        = 0; /* the input for "-n"                   */
    int           opt        = 0; /* option char                          */

    /* Ensure the # of arguments is as expected */
    if (argc != 5) {
        usage();
        exit(EXIT_FAILURE);
    } /* end if */

    /* Parse command line options */
    while ((opt = getopt(argc, argv, "n:i:")) != -1) {
        switch (opt) {
            case 'n':
                if ((temp = atoi(optarg)) < 0) {
                    usage();
                    exit(EXIT_FAILURE);
                } /* end if */
                num = (unsigned int)temp;
                break;
            case 'i':
                if ((temp = atoi(optarg)) < 0) {
                    usage();
                    exit(EXIT_FAILURE);
                } /* end if */
                iterations = (unsigned int)temp;
                break;
            default:
                printf("Invalid option encountered\n");
                break;
        } /* end switch */
    }     /* end while */

    printf("WRITER: # of integers to write = %u; # of iterations = %d\n", num, iterations);

    /* Remove existing data file if needed */
    if (remove(FILENAME) < 0) {
        if (errno == ENOENT)
            printf("WRITER: remove %s--%s\n", FILENAME, strerror(errno));
        else {
            printf("WRITER: error from remove: %d--%s\n", errno, strerror(errno));
            goto error;
        } /* end else */
    }
    else
        printf("WRITER: %s is removed\n", FILENAME);

    /* Create the data file */
    if ((fd = open(FILENAME, O_RDWR | O_TRUNC | O_CREAT, 0664)) < 0) {
        printf("WRITER: error from open\n");
        goto error;
    } /* end if */

    /* Allocate buffer for holding data to be written */
    if ((buf = (unsigned int *)malloc(num * sizeof(unsigned int))) == NULL) {
        printf("WRITER: error from malloc\n");
        if (fd >= 0 && close(fd) < 0)
            printf("WRITER: error from close\n");
        goto error;
    } /* end if */

    printf("\n");

    for (i = 1; i <= iterations; i++) {
        printf("WRITER: *****start iteration %u*****\n", i);

        /* Write the series of integers to the file */
        for (n = 0; n < num; n++) {
            size_t seek_pos;

            /* Set up data to be written */
            for (u = 0; u < num; u++)
                buf[u] = n;

            seek_pos = n * sizeof(unsigned int);
            if (sizeof(off_t) < 8) {
                if (seek_pos > INT32_MAX) {
                    printf("WRITER: seek past range for lseek\n");
                    goto error;
                }
            }
            else {
                if (seek_pos > INT64_MAX) {
                    printf("WRITER: seek past range for lseek\n");
                    goto error;
                }
            }

            /* Position the file to the proper location */
            if (lseek(fd, (off_t)seek_pos, SEEK_SET) < 0) {
                printf("WRITER: error from lseek\n");
                goto error;
            } /* end if */

            /* Write the data */
            if ((bytes_wrote = write(fd, buf, ((num - n) * sizeof(unsigned int)))) < 0) {
                printf("WRITER: error from write\n");
                goto error;
            } /* end if */

            /* Verify the bytes written is correct */
            if (bytes_wrote != (ssize_t)((num - n) * sizeof(unsigned int))) {
                printf("WRITER: error from bytes written\n");
                goto error;
            } /* end if */
        }     /* end for */

        printf("WRITER: *****end iteration %u*****\n\n", i);

    } /* end for */

    /* Close the file */
    if (close(fd) < 0) {
        printf("WRITER: error from close\n");
        goto error;
    } /* end if */

    /* Free the buffer */
    if (buf)
        free(buf);

    return EXIT_SUCCESS;

error:
    return EXIT_FAILURE;
} /* end main() */

#else /* WIN32 / MINGW32 */

int
main(void)
{
    printf("Non-POSIX platform. Exiting.\n");
    return EXIT_FAILURE;
} /* end main() */

#endif /* WIN32 / MINGW32 */
