platypus/buffer.c

561 lines
16 KiB
C
Raw Normal View History

2017-01-30 17:29:09 +00:00
/*
* File Name: buffer.c
2017-02-02 02:06:45 +00:00
* Compiler: GCC 6.3.0
2017-01-30 17:29:09 +00:00
* Author: Victor Fernandes, 040772243
* Course: CST8152 - Compilers, Lab Section: 011
2017-01-30 19:29:38 +00:00
* Date: February 1, 2017
2017-01-31 20:15:51 +00:00
* Professor: Svillen Ranev
2017-01-30 17:29:09 +00:00
* A character buffer utility with three modes of self-incrementation
through dynamic memory allocation, and ability to set a mark flag.
2017-02-02 22:04:10 +00:00
* Function list: b_create, b_isfull, b_isempty, b_size, b_capacity,
b_mode, b_incfactor, b_mark, b_flag, b_getcoffset, b_cbhead, b_setmark,
b_eob, b_addc, b_getc, b_print, b_load, b_reset, b_retract, b_pack, b_free
2017-01-30 17:29:09 +00:00
*/
#include <limits.h>
#include <stdlib.h>
#include "buffer.h"
/* Initializes and allocates memory for the buffer descriptor
* Author: Victor Fernandes
* Version: 0.0.1
* Called functions: calloc(), malloc(), free()
* Parameters:
- short init_capacity (0 - SHRT_MAX)
2017-01-31 20:15:51 +00:00
- char inc_factor (1 to 100 for multiplicative mode, 1 to 255 in additive mode, 0 for fixed mode)
- char o_mode (m, a, f)
2017-01-30 17:29:09 +00:00
* Return values: pBuffer or NULL
* Algorithm: Allocates memory for the buffer descriptor. If successful, do bound
2017-01-30 19:31:56 +00:00
checks on function parameters and assign them to the buffer's variables. If
all is clear, allocate the character buffer. Otherwise return NULL.
2017-01-30 17:29:09 +00:00
*/
Buffer* b_create(short init_capacity, char inc_factor, char o_mode) {
pBuffer pBD; /* Pointer to buffer descriptor */
/* BEGIN CONFIGURING BUFFER */
2017-01-30 19:29:38 +00:00
/* Check if init_capacity is within acceptable range */
2017-01-31 20:15:51 +00:00
if (init_capacity <= ZERO_CAPACITY) {
2017-01-30 17:29:09 +00:00
return NULL;
}
2017-01-30 19:29:38 +00:00
/* Leaving cb_head allocation for last in the event of bad input */
2017-01-30 17:29:09 +00:00
2017-01-30 19:29:38 +00:00
2017-01-30 17:29:09 +00:00
/* Memory allocation */
pBD = (Buffer *) calloc(1, sizeof(Buffer));
if (!pBD) {
return NULL; /* Abort execution immediatelly if allocation fails */
}
/* Check and set operation mode */
if (o_mode == 'f' || inc_factor == FIX_INC_FACTOR) {
pBD->mode = FIX_OP_MODE;
pBD->inc_factor = FIX_INC_FACTOR;
2017-01-31 20:15:51 +00:00
} else if (o_mode == 'a'){
2017-01-30 17:29:09 +00:00
pBD->mode = ADD_OP_MODE;
pBD->inc_factor = inc_factor;
2017-01-30 19:29:38 +00:00
} else if (o_mode == 'm'
&& (inc_factor >= MIN_MUL_INC_FACTOR)
&& (inc_factor <= MAX_MUL_INC_FACTOR)) {
2017-01-30 17:29:09 +00:00
pBD->mode = MUL_OP_MODE;
pBD->inc_factor = inc_factor;
2017-01-30 19:29:38 +00:00
} else { /* Abort everything if any parameters are bad */
2017-01-30 17:29:09 +00:00
free(pBD);
2017-01-30 19:29:38 +00:00
return NULL;
2017-01-30 17:29:09 +00:00
}
2017-01-30 19:29:38 +00:00
/* Attempt to initialize cb_head */
pBD->cb_head = (char *) malloc(sizeof(char) * init_capacity);
/* Abort configuration if allocation fails and release memory */
if (!pBD->cb_head) {
free(pBD);
return NULL;
}
2017-01-30 17:29:09 +00:00
pBD->capacity = init_capacity;
/* END CONFIGURING BUFFER */
return pBD;
}
/* Reports whether the character buffer is full or not
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return values: -1 (R_FAIL1), 0 (FALSE), 1 (TRUE)
2017-01-30 17:29:09 +00:00
*/
int b_isfull(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
2017-01-31 20:15:51 +00:00
if (b_size(pBD) == b_capacity(pBD)) {
2017-01-30 17:29:09 +00:00
return TRUE;
} else {
return FALSE;
}
}
/* Reports if character buffer is empty
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: -1 (R_FAIL1), 0 (FALSE), 1 (TRUE)
2017-01-30 17:29:09 +00:00
*/
int b_isempty(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
if (pBD->addc_offset == OFFSET_RESET) {
return TRUE;
} else {
return FALSE;
}
}
/* Reports the current size of the character buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: -1 (R_FAIL1), short
2017-01-30 17:29:09 +00:00
*/
short b_size(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
return (unsigned short) pBD->addc_offset;
2017-01-30 17:29:09 +00:00
}
/* Reports the current capacity of the character buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: -1 (R_FAIL1), short
2017-01-30 17:29:09 +00:00
*/
short b_capacity(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
return pBD->capacity;
}
/* Reports the current character buffer's operational mode
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: -2 (R_FAIL2),
* -1 (multiplicative),
* 0 (fixed),
* 1 (additive)
2017-01-30 17:29:09 +00:00
*/
int b_mode(Buffer* const pBD) {
if (!pBD) { return R_FAIL2; }
return pBD->mode;
}
/* Returns the non-negative value of the increment factor of the buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: size_t, 256 (ERR_INC_FACTOR)
2017-01-30 17:29:09 +00:00
*/
size_t b_incfactor(Buffer* const pBD) {
2017-01-31 20:15:51 +00:00
if (!pBD) { return ERR_INC_FACTOR; }
return (size_t)(unsigned char) pBD->inc_factor;
2017-01-30 17:29:09 +00:00
}
/* Reports the current position of the mark offset in the character buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: short, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
short b_mark(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
return pBD->mark_offset;
}
/* Reports if the character buffer's memory space was relocated after resizing
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: char, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
char b_flag(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
return pBD->r_flag;
}
/* Reports character buffer's current character offset
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: short, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
short b_getcoffset(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
return pBD->getc_offset;
}
/* Returns the buffer's actual character buffer address
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: char*, NULL
2017-01-30 17:29:09 +00:00
*/
char* b_cbhead(Buffer* const pBD) {
if (!pBD || !pBD->cb_head) { return NULL; }
return pBD->cb_head;
}
2017-02-02 22:04:10 +00:00
/* Sets a mark offset on the buffer's mark flag and returns a pointer
to cb_head at that offset
2017-01-30 17:29:09 +00:00
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: char*, NULL
2017-01-30 17:29:09 +00:00
*/
2017-01-31 20:15:51 +00:00
char* b_setmark(Buffer* const pBD, short mark) {
if (!pBD || mark < 0 || mark > pBD->addc_offset) { return NULL; }
2017-01-30 17:29:09 +00:00
pBD->mark_offset = mark;
2017-01-31 20:15:51 +00:00
return (pBD->cb_head) + pBD->mark_offset;
2017-01-30 17:29:09 +00:00
}
/* Reports the end-of-buffer flag state of the character buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: 1, 0, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
2017-01-31 13:31:36 +00:00
int b_eob(Buffer* const pBD) {
2017-01-30 17:29:09 +00:00
if (!pBD) { return R_FAIL1; }
return pBD->eob;
}
/* Adds one character symbol to the character buffer, incrementing its size if
possible and needed
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: b_isfull(), realloc()
* Parameters:
- pBuffer const pBD
- char symbol (1-255)
2017-02-02 02:06:45 +00:00
* Return value: pBuffer, NULL
* Algorithm: Function checks if the cb_head's character size reached
* maximum capacity and resizes if it's able (by the mode or SHRT_MAX),
* then adds one character to cb_head.
2017-01-30 17:29:09 +00:00
*/
2017-01-31 20:15:51 +00:00
pBuffer b_addc(pBuffer const pBD, char symbol) {
2017-01-30 17:29:09 +00:00
/* Variables used for calculating required space for reallocating cb_head
for additive or multiplicative modes */
short avail_space, new_inc, new_cap = 0;
/* These pointers are used to compare the address of cb_head, in the event it is
moved to a new address space in memory after reallocation*/
char *old_addr;
char *tmp_addr;
2017-01-31 20:15:51 +00:00
if (!pBD) {
2017-01-30 17:29:09 +00:00
return NULL;
}
/* Reset reallocation flag */
2017-01-30 17:29:09 +00:00
pBD->r_flag = UNSET_R_FLAG;
/* BEGIN BUFFER INCREMENT */
if (pBD->addc_offset >= pBD->capacity){
if (pBD->mode == FIX_OP_MODE) { /* Fixed mode, won't increment */
2017-01-30 17:29:09 +00:00
return NULL;
}
else if (pBD->mode == ADD_OP_MODE) { /* Calculate new size for additive mode */
new_cap = pBD->capacity + (unsigned char) pBD->inc_factor;
/* Make sure no short overflow happened */
2017-01-30 17:29:09 +00:00
if (new_cap < MIN_CAPACITY){
return NULL;
}
2017-01-30 17:29:09 +00:00
}
else if (pBD->mode == MUL_OP_MODE) { /* Calculate new size in multiplicative mode */
if (pBD->capacity == SHRT_MAX){ /* Do nothing if at maximum size */
2017-01-30 17:29:09 +00:00
return NULL;
}
/* msvc warns about possible loss of data when converting from double to short, this is fine
because the buffer doesn't deal with floating points*/
2017-01-30 17:29:09 +00:00
avail_space = SHRT_MAX - pBD->capacity;
new_inc = (short) (avail_space * (((double) pBD->inc_factor) / 100));
/* Check if there is enough space for the new increment and
"trim" it if needed */
if (new_inc >= avail_space || (new_inc <= MIN_CAPACITY && pBD->capacity < SHRT_MAX)) {
2017-01-30 17:29:09 +00:00
new_cap = SHRT_MAX;
}
else {
new_cap = pBD->capacity + new_inc;
}
}
/* Reallocate memory to character buffer */
old_addr = pBD->cb_head; /* Keep track of old pointer address to check if it changed */
2017-01-30 17:29:09 +00:00
tmp_addr = (char *)realloc(pBD->cb_head, sizeof(char) * new_cap);
2017-01-30 17:29:09 +00:00
if (tmp_addr == NULL){
return NULL; /* Abort everything if allocation fails */
}
2017-01-30 17:29:09 +00:00
pBD->cb_head = tmp_addr;
pBD->capacity = new_cap;
if (old_addr == pBD->cb_head) { /* Compare the old and new addresses and set flag appropriately */
pBD->r_flag = SET_R_FLAG;
}
} /* END BUFFER INCREASE */
/* Finally, add new symbol to the buffer after increasing it (or not) */
pBD->cb_head[pBD->addc_offset] = symbol;
pBD->addc_offset++;
return pBD;
}
/* Gets one character symbol from the buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: char, -2 (R_FAIL2), -1 (R_FAIL1)
* Algorithm: Function will check if getc_offset is at the end
* of the character array. If so, set flag and return failure.
* Otherwise fetch character from cb_head, increment offset, and
* check if it reached the EOB. If so, apply the flag before
* returning the character.
2017-01-30 17:29:09 +00:00
*/
char b_getc(Buffer* const pBD) {
2017-02-01 23:54:00 +00:00
char char_buf;
2017-01-30 17:29:09 +00:00
if (!pBD) { return R_FAIL2; }
2017-02-02 22:04:10 +00:00
/* Make sure the buffer isn't at the end of the read offset*/
2017-01-31 20:24:18 +00:00
if (pBD->getc_offset == pBD->addc_offset) {
2017-01-30 17:29:09 +00:00
pBD->eob = SET_EOB_FLAG;
return R_FAIL1;
} else {
pBD->eob = UNSET_EOB_FLAG;
}
2017-02-02 22:04:10 +00:00
/* Fetch character at read offset if EOB check passed */
2017-02-01 23:54:00 +00:00
char_buf = pBD->cb_head[pBD->getc_offset++];
2017-02-02 22:04:10 +00:00
/* Double check if it is at EOB and set flag if needed.
Standard behaviour for a buffer/file stream
*/
2017-02-01 23:54:00 +00:00
if (pBD->getc_offset == pBD->addc_offset) {
pBD->eob = SET_EOB_FLAG;
}
return char_buf;
2017-01-30 17:29:09 +00:00
}
/* Prints the output of the buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: b_isempty(), b_getc(), b_eob(), printf()
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: int, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
int b_print(Buffer* const pBD) {
int char_count = 0; /* Counter to track how many characters were sent to output */
char char_buf; /* "Buffer" character to load before output */
short tmp_offset;
2017-01-30 17:29:09 +00:00
2017-01-31 20:15:51 +00:00
if (!pBD || !pBD->cb_head) {
return R_FAIL1;
}
if (b_isempty(pBD) == TRUE) {
printf("The buffer is empty.\n");
}
2017-01-30 17:29:09 +00:00
else {
/* Save getc_offset to restore after printing */
2017-01-30 21:35:51 +00:00
tmp_offset = pBD->getc_offset;
2017-01-30 17:29:09 +00:00
pBD->getc_offset = OFFSET_RESET;
do {
2017-01-30 17:29:09 +00:00
char_buf = b_getc(pBD);
printf("%c", (char) char_buf);
char_count++;
} while (!b_eob(pBD));
2017-01-30 17:29:09 +00:00
printf("\n");
/* Restore the getc_offset */
pBD->getc_offset = tmp_offset;
2017-01-30 17:29:09 +00:00
}
return char_count;
}
/* Loads symbols from a file to the buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
2017-01-31 13:31:36 +00:00
* Called functions: fgetc, feof, b_addc
2017-01-30 17:29:09 +00:00
* Parameters:
- FILE* const fi
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: int, -1 (R_FAIL1), -2 (LOAD_FAIL)
2017-01-30 17:29:09 +00:00
*/
int b_load(FILE* const fi, Buffer* const pBD) {
char char_buf; /* "Buffer" character to be loaded before adding to cb_head */
int char_count = 0; /* Character counter */
if (!fi || !pBD) {
2017-01-31 20:15:51 +00:00
return R_FAIL1;
}
2017-01-30 17:29:09 +00:00
2017-01-31 20:15:51 +00:00
while (!feof(fi)) {
2017-01-30 17:29:09 +00:00
char_buf = (char) fgetc(fi);
2017-02-02 22:04:10 +00:00
/* Check loaded character if it's at the end*/
2017-01-31 20:15:51 +00:00
if (char_buf == EOF) {
2017-01-30 17:29:09 +00:00
break;
}
2017-02-02 22:04:10 +00:00
/* Stop loading if adding a character fails */
if (!b_addc(pBD, char_buf)) {
2017-01-30 17:29:09 +00:00
return LOAD_FAIL;
}
char_count++;
2017-01-31 20:15:51 +00:00
}
2017-01-30 17:29:09 +00:00
return char_count;
}
/* Resets the buffer
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: 1 (TRUE), -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
int b_reset(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
pBD->addc_offset = OFFSET_RESET;
pBD->getc_offset = OFFSET_RESET;
pBD->mark_offset = OFFSET_RESET;
pBD->eob = UNSET_EOB_FLAG;
2017-01-31 20:15:51 +00:00
pBD->r_flag = UNSET_R_FLAG;
2017-01-30 17:29:09 +00:00
return TRUE;
}
/* Retracts the buffer's character offset to the mark
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: short, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
short b_retract_to_mark(Buffer* const pBD) {
/* Check if any offsets are out of bounds */
if(!pBD ||
2017-02-02 02:06:45 +00:00
pBD->mark_offset < OFFSET_RESET ||
pBD->mark_offset > pBD->capacity){
2017-01-30 17:29:09 +00:00
return R_FAIL1;
}
pBD->getc_offset = pBD->mark_offset;
return pBD->getc_offset;
}
/* Retracts the buffer's character offset one character backwards
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: short, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
short b_retract(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
else if (pBD->getc_offset > OFFSET_RESET) {
pBD->getc_offset--;
}
return pBD->getc_offset;
}
/* Returns the buffer's memory reallocation flag
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: N/A
* Parameters:
- pBuffer const pBD
2017-02-02 02:06:45 +00:00
* Return value: char, -1 (R_FAIL1)
2017-01-30 17:29:09 +00:00
*/
char b_rflag(Buffer* const pBD) {
if (!pBD) { return R_FAIL1; }
return pBD->r_flag;
}
/* Reduce the size of the character buffer to free up memory
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: realloc()
* Parameters:
- pBuffer const pBD
* Return value: Buffer* const
*/
Buffer* b_pack(Buffer* const pBD) {
short new_cap; /* Used to set the new buffer capacity value */
/* Temporary pointers used to compare old and new pointer addresses */
char *old_addr;
char *tmp_addr;
if (!pBD || !pBD->cb_head) { return NULL; }
/* Configure new capacity value */
new_cap = pBD->addc_offset + 1;
2017-02-02 02:06:45 +00:00
/* Avoid buffer destruction from overflow */
if (new_cap <= 0) { return NULL;}
2017-01-30 17:29:09 +00:00
old_addr = pBD->cb_head;
/* Reallocate cb_head to new size */
tmp_addr = realloc(pBD->cb_head, sizeof(char) * new_cap);
if (!tmp_addr){ return NULL; } /* Abort if realloc failed */
/* Assign new address to cb_head and reassign capacity value */
pBD->cb_head = tmp_addr;
pBD->capacity = new_cap;
/* Compare old and new addresses and set R_FLAG accordingly */
if (old_addr != pBD->cb_head){
pBD->r_flag = SET_R_FLAG;
}
return pBD;
}
/* Deallocate and release all memory used by the buffer.
* Author: Victor Fernandes, 040772243
* Version: 0.0.1
* Called functions: free()
* Parameters:
- pBuffer const pBD
* Return value: N/A
*/
void b_free(Buffer* const pBD) {
free(pBD->cb_head);
free(pBD);
}