---
# System prepended metadata

title: 'Assignment 6: Low-level refactoring and performance Code'
tags: [CS35L]

---

# Assignment 6: Low-level refactoring and performance Code

Benefits of refactoring:
- Readability and modularity
- Easier to debug
- Reduces compilation time (parallel compilation of multiple files)


### randall.c edited
```c=
#include <errno.h>
#include <stdlib.h>
#include "options.h"
#include "output.h"
#include "rand64-hw.h"
#include "rand64-sw.h"
#include "my-lrand48-r.h"

/* Main program, which outputs N bytes of random data.  */
int main (int argc, char **argv) {
    long long nbytes = 0;
    int inputSize = 100; 
    int outputSize = 100; 
    char* input = malloc(inputSize * sizeof(char));
    char* output = malloc(outputSize * sizeof(char));
    bool stopped = false;

    options(argc, argv, &nbytes, input, output, &stopped);

    if (stopped) {
        free(input);
        free(output);
        return 1;
    }

    // Use nbytes, input, and output as needed
    //printf("nbytes: %lld\n", nbytes);
    //printf("input: %s\n", input);
    //printf("output: %s\n", output);

    if (strcmp(input, "rdrand") == 0 && !rdrand_supported ()) { //check for working hardware
      fprintf (stderr, "hardward random-number generation is not available!\n");
      free(input);
      free(output);
      return 1; 
    }
  /* If there's no work to do, don't worry about which library to use.  */
  if (nbytes == 0) {
      free(input);
      free(output);
      return 0;
  }

  /* Now that we know we have work to do, arrange to use the
     appropriate library.  */
  void (*initialize) (void);
  unsigned long long (*rand64) (void);
  void (*finalize) (void);
  if (rdrand_supported ())
    {
      initialize = hardware_rand64_init;
      rand64 = hardware_rand64;
      finalize = hardware_rand64_fini;
    }
  else if (strcmp(input, "lrand48_r")) 
    {
      initialize = lrand48_r_init;
      rand64 = my_lrand48_r;
      finalize = lrand48_r_fini;
    }
  else
    {
      if (input[0] == '/')
        setPath(input); //change path 
      initialize = software_rand64_init;
      rand64 = software_rand64;
      finalize = software_rand64_fini;
    }

  initialize ();
  int wordsize = sizeof rand64 ();
  int output_errno = 0;

  do
    {
      unsigned long long x = rand64 ();
      int outbytes = nbytes < wordsize ? nbytes : wordsize;
      if (!writebytes (x, outbytes, output))
	{
	  output_errno = errno;
	  break;
	}
      nbytes -= outbytes;
    }
  while (0 < nbytes);

  if (fclose (stdout) != 0)
    output_errno = errno;

  if (output_errno)
    {
      errno = output_errno;
      perror ("output");
    }

  finalize ();
  free(input);
  free(output);
  return !!output_errno;
}

```

### options.h
```c=
#ifndef OPTIONS_H
#define OPTIONS_H

#include <stdbool.h>

bool isPosDec(char *arg);

void options(int argc, char **argv, long long *nbytes, char *input, char *output, bool *stopped);

#endif /* OPTIONS_H */
```

### options.c
```c= 
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

bool isPosDec(char *num) {
    while (*num) {
        if (!isdigit(*num)) {
            return false; // Non-digit character found
        }
        num++;
    }
    
    return true;
}

void options(int argc, char **argv, long long *nbytes, char *input, char *output, bool *stopped) {
    int opt; 
    strcpy(input, "rdrand");
    strcpy(output, "stdio");

    bool inputErr = false; 
    bool outputErr = false; 
    bool bytesErr = false; 
    while ((opt = getopt(argc, argv, ":i:o:")) != -1) {
        switch (opt) {
            case 'i':
                if (strcmp(optarg, "rdrand") == 0 || strcmp(optarg, "lrand48_r") == 0 || (optarg[0] == '/' && access(optarg, F_OK) != -1))
                    strcpy(input, optarg); //get arg input
                else 
                    inputErr = true;
                break;
            case 'o':
                if (strcmp(optarg, "stdio") == 0 || isPosDec(optarg))
                    strcpy(output, optarg); //get arg output
                else
                    outputErr = true;
                break;
            default:
                fprintf (stderr, "Usage: %s [nBYTES] [-i input] [-o output]\n", argv[0]);
                *stopped = true;
        return; 
        }
        if (inputErr) { //stop early if there is an error detected
            fprintf (stderr, "%s: usage: %s -i INPUT=rdrand, lrand48, /F\n", argv[0], argv[0]);
            *stopped = true;
            return;
        } else if (outputErr) {
            fprintf (stderr, "%s: usage: %s -o OUTPUT=stdio, N (pos decimal integer)\n", argv[0], argv[0]);
            *stopped = true;
            return;
        }
    }

    if (optind < argc) { //for no arg cases like NBYTES
        char *endptr; //original case
        errno = 0;
        *nbytes = strtoll (argv[optind], &endptr, 10);
        if (errno)
            perror (argv[optind]);
        else
            bytesErr = !*endptr && 0 <= *nbytes;
    }

    if (!bytesErr) {
            fprintf (stderr, "%s: usage: %s NBYTES\n", argv[0], argv[0]);
            *stopped = true;
            return;
    }
}
```


### Makefile
```c=
# Optimization level.  Change this -O2 to -Og or -O0 or whatever.
OPTIMIZE = 

src-files = *.c 
header-files = *.h
# The C compiler and its options.
CC = gcc
CFLAGS = $(OPTIMIZE) -g3 -Wall -Wextra -fanalyzer \
  -march=native -mtune=native -mrdrnd

# The archiver command, its options and filename extension.
TAR = tar
TARFLAGS = --gzip --transform 's,^,randall/,'
TAREXT = tgz

default: randall

randall: $(src-files) $(header-files)
	$(CC) $(CFLAGS) $(src-files) $(header-files) -o $(@)

assignment: randall-assignment.$(TAREXT)
assignment-files = COPYING Makefile randall.c 
randall-assignment.$(TAREXT): $(assignment-files)
	$(TAR) $(TARFLAGS) -cf $@ $(assignment-files)

submission-tarball: randall-submission.$(TAREXT)
submission-files = $(assignment-files) \
  notes.txt *.c *.h rand.data
randall-submission.$(TAREXT): $(submission-files)
	$(TAR) $(TARFLAGS) -cf $@ $(submission-files)

repository-tarball:
	$(TAR) -czf randall-git.tgz .git

check: randall
	$(make randall)

	@if [ $$(./randall 26 | wc -c) -ne 26 ]; then \
		echo "Test of length failed! Expected 26, got $$(./randall 26 | wc -c)"; \
	elif [ $$(./randall -75 -e | wc -c) -ne 0 ]; then \
		echo "Test of incorrect argument failed"; \
	elif [ $$(./randall 50 -e | wc -c) -eq 50 ]; then \
		echo "Test of incorrect argument failed"; \
	elif [ $$(./randall 25 -i wrong_input | wc -c) -eq 25 ]; then \
		echo "#1 Test of input argument failed"; \
	elif [ $$(./randall -i rdrand 12| wc -c) -ne 12 ]; then \
		echo "#2 Test of input argument failed"; \
	elif [ $$(./randall -i lrand48_r 13| wc -c) -ne 13 ]; then \
		echo "#3 Test of input argument failed"; \
	elif [ $$(./randall -i /dev/random 14| wc -c) -ne 14 ]; then \
		echo "#4 Test of input argument failed"; \
	elif [ $$(./randall -i /dev/urandom 15| wc -c) -ne 15 ]; then \
		echo "#5 Test of input argument failed"; \
	elif [ $$(./randall -i /dev/jk 16| wc -c) -eq 16 ]; then \
		echo "#6 Test of input argument failed"; \
	elif [ $$(./randall 75 -o wrong_output | wc -c) -eq 75 ]; then \
		echo "#1 Test of output argument failed"; \
	elif [ $$(./randall 17 -o stdio | wc -c) -ne 17 ]; then \
		echo "#2 Test of input argument failed"; \
	elif [ $$(./randall 18 -o 13 | wc -c) -ne 18 ]; then \
		echo "#3 Test of input argument failed"; \
	elif [ $$(./randall 98 -o 13 -i /dev/urandom | wc -c) -ne 98 ]; then \
		echo "full test failed!"; \
	fi


.PHONY: default clean assignment submission-tarball repository-tarball

clean:
	rm -f *.o *.$(TAREXT) randall
```

### randall.c original
```c=
#include <cpuid.h>
#include <errno.h>
#include <immintrin.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

/* Hardware implementation.  */

/* Description of the current CPU.  */
struct cpuid { unsigned eax, ebx, ecx, edx; };

/* Return information about the CPU.  See <http://wiki.osdev.org/CPUID>.  */
static struct cpuid
cpuid (unsigned int leaf, unsigned int subleaf)
{
  struct cpuid result;
  asm ("cpuid"
       : "=a" (result.eax), "=b" (result.ebx),
	 "=c" (result.ecx), "=d" (result.edx)
       : "a" (leaf), "c" (subleaf));
  return result;
}

/* Return true if the CPU supports the RDRAND instruction.  */
static _Bool
rdrand_supported (void)
{
  struct cpuid extended = cpuid (1, 0);
  return (extended.ecx & bit_RDRND) != 0;
}

/* Initialize the hardware rand64 implementation.  */
static void
hardware_rand64_init (void)
{
}

/* Return a random value, using hardware operations.  */
static unsigned long long
hardware_rand64 (void)
{
  unsigned long long int x;

  /* Work around GCC bug 107565
     <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107565>.  */
  x = 0;

  while (! _rdrand64_step (&x))
    continue;
  return x;
}

/* Finalize the hardware rand64 implementation.  */
static void
hardware_rand64_fini (void)
{
}



/* Software implementation.  */

/* Input stream containing random bytes.  */
static FILE *urandstream;

/* Initialize the software rand64 implementation.  */
static void
software_rand64_init (void)
{
  urandstream = fopen ("/dev/random", "r");
  if (! urandstream)
    abort ();
}

/* Return a random value, using software operations.  */
static unsigned long long
software_rand64 (void)
{
  unsigned long long int x;
  if (fread (&x, sizeof x, 1, urandstream) != 1)
    abort ();
  return x;
}

/* Finalize the software rand64 implementation.  */
static void
software_rand64_fini (void)
{
  fclose (urandstream);
}

static bool
writebytes (unsigned long long x, int nbytes)
{
  do
    {
      if (putchar (x) < 0)
	return false;
      x >>= CHAR_BIT;
      nbytes--;
    }
  while (0 < nbytes);

  return true;
}

/* Main program, which outputs N bytes of random data.  */
int
main (int argc, char **argv)
{
  /* Check arguments.  */
  bool valid = false;
  long long nbytes;
  if (argc == 2)
    {
      char *endptr;
      errno = 0;
      nbytes = strtoll (argv[1], &endptr, 10);
      if (errno)
	perror (argv[1]);
      else
	valid = !*endptr && 0 <= nbytes;
    }
  if (!valid)
    {
      fprintf (stderr, "%s: usage: %s NBYTES\n", argv[0], argv[0]);
      return 1;
    }

  /* If there's no work to do, don't worry about which library to use.  */
  if (nbytes == 0)
    return 0;

  /* Now that we know we have work to do, arrange to use the
     appropriate library.  */
  void (*initialize) (void);
  unsigned long long (*rand64) (void);
  void (*finalize) (void);
  if (rdrand_supported ())
    {
      initialize = hardware_rand64_init;
      rand64 = hardware_rand64;
      finalize = hardware_rand64_fini;
    }
  else
    {
      initialize = software_rand64_init;
      rand64 = software_rand64;
      finalize = software_rand64_fini;
    }

  initialize ();
  int wordsize = sizeof rand64 ();
  int output_errno = 0;

  do
    {
      unsigned long long x = rand64 ();
      int outbytes = nbytes < wordsize ? nbytes : wordsize;
      if (!writebytes (x, outbytes))
	{
	  output_errno = errno;
	  break;
	}
      nbytes -= outbytes;
    }
  while (0 < nbytes);

  if (fclose (stdout) != 0)
    output_errno = errno;

  if (output_errno)
    {
      errno = output_errno;
      perror ("output");
    }

  finalize ();
  return !!output_errno;
}

```