How to modify or create a struct depending on argument? - c

I must further develop a simulator using C, capable of simulating different cache types (direct, n-way associative, fully associative). Right now my code works in the sense that it can simulate a direct-mapped cache, however it cannot simulate any other type.
My Code
My C file:
/*
* CS3375 Computer Architecture
* Course Project
* Cache Simulator Design and Development
* FALL 2017
* By Yong Chen
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "cachesim.h"
int main(int argc, char *argv[])
{
char type;
if (argc != 3) {
printf("Usage: %s <direct> <trace file name>\n", argv[0]);
return 1;
}
#ifdef DBG
printf("BLOCK SIZE = %d Bytes\n", BLOCK_SIZE);
printf("%d-WAY\n", WAY_SIZE);
printf("CACHE SIZE = %d Bytes\n", CACHE_SIZE);
printf("NUMBER OF BLOCKS = %d\n", NUM_BLOCKS);
printf("NUMBER OF SETS = %d\n", NUM_SETS);
printf("\n");
#endif
struct direct_mapped_cache d_cache;
char* trace_file_name = argv[2];
char mem_request[20];
uint64_t address;
FILE *fp;
/* Initialization */
for (int i=0; i<NUM_BLOCKS; i++) {
d_cache.valid_field[i] = 0;
d_cache.dirty_field[i] = 0;
d_cache.tag_field[i] = 0;
}
d_cache.hits = 0;
d_cache.misses = 0;
/* Opening the memory trace file */
fp = fopen(trace_file_name, "r");
/*Checks if argument specified direct-mapped cache*/
if (strncmp(argv[1], "direct", 6)==0) { /* Simulating direct-mapped cache */
/* Read the memory request address and access the cache */
while (fgets(mem_request, 20, fp)!= NULL) {
address = convert_address(mem_request);
direct_mapped_cache_access(&d_cache, address);
}
/*Calculate Hit and Miss Rate*/
double hit_rate = ((1.0 * d_cache.hits)/(d_cache.hits + d_cache.misses));
double miss_rate = ((1.0 * d_cache.misses)/(d_cache.hits + d_cache.misses));
/*Print out the results*/
printf("\n==================================\n");
printf("Cache type: Direct-Mapped Cache\n");
printf("==================================\n");
printf("Cache Hits: %d\n", d_cache.hits);
printf("Cache Misses: %d\n", d_cache.misses);
printf("Cache Hit Rate: %f\n", hit_rate);
printf("Cache Miss Rate: %f\n", miss_rate);
printf("\n");
}
fclose(fp);
return 0;
}
uint64_t convert_address(char memory_addr[])
/* Converts the physical 32-bit address in the trace file to the "binary" \\
* (a uint64 that can have bitwise operations on it) */
{
uint64_t binary = 0;
int i = 0;
while (memory_addr[i] != '\n') {
if (memory_addr[i] <= '9' && memory_addr[i] >= '0') {
binary = (binary*16) + (memory_addr[i] - '0');
} else {
if(memory_addr[i] == 'a' || memory_addr[i] == 'A') {
binary = (binary*16) + 10;
}
if(memory_addr[i] == 'b' || memory_addr[i] == 'B') {
binary = (binary*16) + 11;
}
if(memory_addr[i] == 'c' || memory_addr[i] == 'C') {
binary = (binary*16) + 12;
}
if(memory_addr[i] == 'd' || memory_addr[i] == 'D') {
binary = (binary*16) + 13;
}
if(memory_addr[i] == 'e' || memory_addr[i] == 'E') {
binary = (binary*16) + 14;
}
if(memory_addr[i] == 'f' || memory_addr[i] == 'F') {
binary = (binary*16) + 15;
}
}
i++;
}
#ifdef DBG
printf("%s converted to %llu\n", memory_addr, binary);
#endif
return binary;
}
void direct_mapped_cache_access(struct direct_mapped_cache *cache, uint64_t address)
{
uint64_t block_addr = address >> (unsigned)log2(BLOCK_SIZE);
uint64_t index = block_addr % NUM_BLOCKS;
uint64_t tag = block_addr >> (unsigned)log2(NUM_BLOCKS);
#ifdef DBG
printf("Memory address: %llu, Block address: %llu, Index: %llu, Tag: %llu ", address, block_addr, index, tag);
#endif
if (cache->valid_field[index] && cache->tag_field[index] == tag) { /* Cache hit */
cache->hits += 1;
#ifdef DBG
printf("Hit!\n");
#endif
} else {
/* Cache miss */
cache->misses += 1;
#ifdef DBG
printf("Miss!\n");
#endif
if (cache->valid_field[index] && cache->dirty_field[index]) {
/* Write the cache block back to memory */
}
cache->tag_field[index] = tag;
cache->valid_field[index] = 1;
cache->dirty_field[index] = 0;
}
}
My .h file:
/*
* CS3375 Computer Architecture
* Course Project
* Cache Simulator Design and Development
* FALL 2017
* By Yong Chen
*/
#include <stdio.h>
/* Cache block size (or cache line size) in bytes*/
#define BLOCK_SIZE 64 /*(must be power of 2). 4 Bytes = 1 Word NOTE: MUST CHANGE DEPENDING ON TYPE*/
#define WAY_SIZE 1 /* Associativity; 1-way = direct-mapped MUST CHANGE DEPENDING ON TYPE*/
#define CACHE_SIZE 32768 /* Cache capacity in bytes (must be power of 2) THIS WILL STAY FIXED*/
#define NUM_BLOCKS (CACHE_SIZE / BLOCK_SIZE)
#define NUM_SETS (BLOCK_SIZE/WAY_SIZE)
/*For fully associative, num sets is equal to num blocks because way size is equal to num blocks. */
/*MAY TRY LEAVING THESE VARIABLES UNDEFINED, AND THEY WILL BE SET DEPENDING ON USER INPUT.*/
#define DBG /*Prints debugging information*/
/*The data structure of direct-mapped cache*/
struct direct_mapped_cache {
unsigned valid_field[NUM_BLOCKS]; /* Valid field */
unsigned dirty_field[NUM_BLOCKS]; /* Dirty field; since we don't distinguish writes and \\
reads in this project yet, this field doesn't really matter */
uint64_t tag_field[NUM_BLOCKS]; /* Tag field */
char data_field[NUM_BLOCKS][BLOCK_SIZE]; /* Data field; since we don't really fetch data, \\
this field doesn't really matter */
int hits; /* Hit count */
int misses; /* Miss count */
};
/*Read the memory traces and convert it to binary*/
uint64_t convert_address(char memory[]);
/*Simulate the direct-mapped cache*/
void direct_mapped_cache_access(struct direct_mapped_cache *cache, uint64_t address);
What I've Tried
Admittedly, I am a beginner when it comes to the C language, so my solution may be simpler than I think, but I've been unable to find any answers thus far. I've considered changing where the cache variables were defined using "#define" depending on the argument, but I have learned that "#define" is run by pre-processing, so this won't work.
I've also tried creating multiple struct classes for each type of cache that needs to be simulated, but since struct variables in C cannot be initialized within the class, I can't get this to work either.
To my understanding, structs in C cannot have constructors either, as I've looked into this as well.
Any help or step in the right direction will be greatly appreciated.

first there is a you are mistaken about struct : they are not object but a data layout which mean no constructor or destructor (if you need those feature you will need dedicated function)
next your actual question: "can i have a variable amount of memory in my struct ?"
Answer:
Yes, but not directly, you will need to use function like malloc and free to dynamically allocate and deallocate memory, i won't make a full explanation about them you will find how to use them very easily online.
You will need to do something like :
#include<stdlib.h>
struct mystruct {
unsigned size;
unsigned * memory;
};
typedef struct mystruct mystruct;
mystruct * mystruct_constructor(unsigned s) {
//allocate the space of struct itself
mystruct * ms = malloc(sizeof(mystruct));
ms->size = s;
//allocate the variable size array
ms->memory = malloc(sizeof(unsigned) * s);
return ms;
}
void mystruct_destructor(mystruct * ms) {
free(ms->memory);
free(ms);
}
just know that malloc don't initialize the space so the data within are unknown (and likely not zero).
as for the why you can't define variable size struct is because you need compile time array (those like unsigned var[X]) to have a compile time defined size, because when you make such array the struct actually contain X element of the array type.
which mean:
#define N whatever_value_you_want
struct {
unsigned item[N + 1]; // valid because it can be known at compile-time
};
has the same (under normal condition) layout as:
struct {
unsigned item_0;
unsigned item_1;
unsigned item_2;
...
unsigned item_N;
}

Related

DES CBC mode not outputting correctly

I am working on a project in C to implement CBC mode on top of a skeleton code for DES with OpenSSL. We are not allowed to use a function that does the CBC mode automatically, in the sense that we must implement it ourselves. I am getting output but I have result files and my output is not matching up completely with the intended results. I also am stuck on figuring out how to pad the file to ensure all the blocks are of equal size, which is probably one of the reasons why I'm not receiving the correct output. Any help would be appreciated. Here's my modification of the skeleton code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
#include <sys/time.h>
#include <unistd.h>
#define ENC 1
#define DEC 0
DES_key_schedule key;
int append(char*s, size_t size, char c) {
if(strlen(s) + 1 >= size) {
return 1;
}
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
return 0;
}
int getSize (char * s) {
char * t;
for (t = s; *t != '\0'; t++)
;
return t - s;
}
void strToHex(const_DES_cblock input, unsigned char *output) {
int arSize = 8;
unsigned int byte;
for(int i=0; i<arSize; i++) {
if(sscanf(input, "%2x", &byte) != 1) {
break;
}
output[i] = byte;
input += 2;
}
}
void doBitwiseXor(DES_LONG *xorValue, DES_LONG* data, const_DES_cblock roundOutput) {
DES_LONG temp[2];
memcpy(temp, roundOutput, 8*sizeof(unsigned char));
for(int i=0; i<2; i++) {
xorValue[i] = temp[i] ^ data[i];
}
}
void doCBCenc(DES_LONG *data, const_DES_cblock roundOutput, FILE *outFile) {
DES_LONG in[2];
doBitwiseXor(in, data, roundOutput);
DES_encrypt1(in,&key,ENC);
printf("ENCRYPTED\n");
printvalueOfDES_LONG(in);
printf("%s","\n");
fwrite(in, 8, 1, outFile);
memcpy(roundOutput, in, 2*sizeof(DES_LONG));
}
int main(int argc, char** argv)
{
const_DES_cblock cbc_key = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
const_DES_cblock IV = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
// Initialize the timing function
struct timeval start, end;
gettimeofday(&start, NULL);
int l;
if ((l = DES_set_key_checked(&cbc_key,&key)) != 0)
printf("\nkey error\n");
FILE *inpFile;
FILE *outFile;
inpFile = fopen("test.txt", "r");
outFile = fopen("test_results.txt", "wb");
if(inpFile && outFile) {
unsigned char ch;
// A char array that will hold all 8 ch values.
// each ch value is appended to this.
unsigned char eight_bits[8];
// counter for the loop that ensures that only 8 chars are done at a time.
int count = 0;
while(!feof(inpFile)) {
// read in a character
ch = fgetc(inpFile);
// print the character
printf("%c",ch);
// append the character to eight_bits
append(eight_bits,1,ch);
// increment the count so that we only go to 8.
count++;
const_DES_cblock roundOutput;
// When count gets to 8
if(count == 8) {
// for formatting
printf("%s","\n");
// Encrypt the eight characters and store them back in the char array.
//DES_encrypt1(eight_bits,&key,ENC);
doCBCenc(eight_bits, roundOutput, outFile);
// prints out the encrypted string
int k;
for(k = 0; k < getSize(eight_bits); k++){
printf("%c", eight_bits[k]);
}
// Sets count back to 0 so that we can do another 8 characters.
count = 0;
// so we just do the first 8. When everything works REMOVE THE BREAK.
//break;
}
}
} else {
printf("Error in opening file\n");
}
fclose(inpFile);
fclose(outFile);
// End the timing
gettimeofday(&end, NULL);
// Initialize seconds and micros to hold values for the time output
long seconds = (end.tv_sec - start.tv_sec);
long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);
// Output the time
printf("The elapsed time is %d seconds and %d microseconds\n", seconds, micros);
}
Your crypto is at least half correct, but you have a lot of actual or potential other errors.
As you identified, raw CBC mode can only encrypt data which is a multiple of the block size, for DES 64 bits or 8 bytes (on most modern computers and all where you could use OpenSSL). In some applications this is okay; for example if the data is (always) an MD5 or SHA-256 or SHA-512 hash, or a GUID, or an IPv6 (binary) address, then it is a block multiple. But most applications want to handle at least any length in bytes, so they need to use some scheme to pad on encrypt and unpad on decrypt the last block (all blocks before the last already have the correct size). Many different schemes have been developed for this, so you need to know which to use. I assume this is a school assignment (since no real customer would set such a stupid and wasteful combination of requirements) and this should either have been specified or clearly left as a choice. One padding scheme very common today (although not for single-DES, because that is broken, unsafe, obsolete, and not common) is the one defined by PKCS5 and generalized by PKCS7 and variously called PKCS5, PKCS7, or PKCS5/7 padding, so I used that as an example.
Other than that:
you try to test feof(inpFile) before doing fgetc(inpFile). This doesn't work in C. It results in your code treating the low 8 bits of EOF (255 aka 0xFF on practically all implementations) as a valid data character added to the characters that were actually in the file. The common idiom is to store the return of getchar/getc/fgetc in a signed int and compare to EOF, but that would have required more changes so I used an alternate.
you don't initialize eight_bits which is a local-scope automatic duration variable, so its contents are undefined and depending on the implementation are often garbage, which means trying to 'append' to it by using strlen() to look for the end won't work right and might even crash. Although on some implementations at least some times it might happen to contain zero bytes, and 'work'. In addition it is possible in C for a byte read from a file (and stored here) to be \0 which will also make this work wrong, although if this file contains text, as its name suggests, it probably doesn't contain any \0 bytes.
once you fill eight_bits you write 'off-the-end' into element [8] which doesn't exist. Technically this is Undefined Behavior and anything at all can happen, traditionally expressed on Usenet as nasal demons. Plus after main finishes the first block it doesn't change anything in eight_bits so all further calls to append find it full and discard the new character.
while you could fix the above points separately, a much simple solution is available: you are already using count to count the number of bytes in the current block, so just use it as the subscript.
roundOutput is also an uninitialized local/auto variable within the loop, which is then used as the previous block for the CBC step, possibly with garbage or wrong value(s). And you don't use the IV at all, as is needed. You should allocate this before the loop (so it retains its value through all iterations) and initialize it to the IV, and then for each block in the loop your doCBCenc can properly XOR it to the new block and then leave the encrypted new block to be used next time.
your code labelled 'prints out the encrypted string' prints plaintext not ciphertext -- which is binary and shouldn't be printed directly anyway -- and is not needed because your file-read loop already echoes each character read. But if you do want to print a (validly null-terminated) string it's easier to just use fputs(s) or [f]printf([f,]"%s",s) or even fwrite(s,1,strlen(s),f).
your doCBCenc has a reference to printvalueofDES_LONG which isn't defined anywhere, and which along with two surrounding printf is clearly not needed.
you should use a cast to convert the first argument to doCBCenc -- this isn't strictly required but is good style and a good compiler (like mine) complains if you don't
finally, when an error occurs you usually print a message but then continue running, which will never work right and may produce symptoms that disguise the problem and make it hard to fix.
The below code fixes the above except that last (which would have been more work for less benefit) plus I removed routines that are now superfluous, and the timing code which is just silly: Unix already has builtin tools to measure and display process time more easily and reliably than writing code. Code I 'removed' is under #if 0 for reference, and code I added under #else or #if 1 except for the cast. The logic for PKCS5/7 padding is under #if MAYBE so it can be either selected or not. Some consider it better style to use sizeof(DES_block) or define a macro instead of the magic 8's, but I didn't bother -- especially since it would have required changes that aren't really necessary.
// SO70209636
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
#include <sys/time.h>
#include <unistd.h>
#define ENC 1
#define DEC 0
DES_key_schedule key;
#if 0
int append(char*s, size_t size, char c) {
if(strlen(s) + 1 >= size) {
return 1;
}
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
return 0;
}
int getSize (char * s) {
char * t;
for (t = s; *t != '\0'; t++)
;
return t - s;
}
void strToHex(const_DES_cblock input, unsigned char *output) {
int arSize = 8;
unsigned int byte;
for(int i=0; i<arSize; i++) {
if(sscanf(input, "%2x", &byte) != 1) {
break;
}
output[i] = byte;
input += 2;
}
}
#endif
void doBitwiseXor(DES_LONG *xorValue, DES_LONG* data, const_DES_cblock roundOutput) {
DES_LONG temp[2];
memcpy(temp, roundOutput, 8*sizeof(unsigned char));
for(int i=0; i<2; i++) {
xorValue[i] = temp[i] ^ data[i];
}
}
void doCBCenc(DES_LONG *data, const_DES_cblock roundOutput, FILE *outFile) {
DES_LONG in[2];
doBitwiseXor(in, data, roundOutput);
DES_encrypt1(in,&key,ENC);
#if 0
printf("ENCRYPTED\n");
printvalueOfDES_LONG(in);
printf("%s","\n");
#endif
fwrite(in, 8, 1, outFile);
memcpy(roundOutput, in, 2*sizeof(DES_LONG));
}
int main(int argc, char** argv)
{
const_DES_cblock cbc_key = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
const_DES_cblock IV = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
#if 0
// Initialize the timing function
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
int l;
if ((l = DES_set_key_checked(&cbc_key,&key)) != 0)
printf("\nkey error\n");
#if 1
DES_cblock roundOutput; // must be outside the loop
memcpy (roundOutput, IV, 8); // and initialized
#endif
FILE *inpFile;
FILE *outFile;
inpFile = fopen("test.txt", "r");
outFile = fopen("test.encrypt", "wb");
if(inpFile && outFile) {
unsigned char ch;
// A char array that will hold all 8 ch values.
// each ch value is appended to this.
unsigned char eight_bits[8];
// counter for the loop that ensures that only 8 chars are done at a time.
int count = 0;
#if 0
while(!feof(inpFile)) {
// read in a character
ch = fgetc(inpFile);
#else
while( ch = fgetc(inpFile), !feof(inpFile) ){
#endif
// print the character
printf("%c",ch);
#if 0
// append the character to eight_bits
append(eight_bits,1,ch);
// increment the count so that we only go to 8.
count++;
#else
eight_bits[count++] = ch;
#endif
#if 0
const_DES_cblock roundOutput;
#endif
// When count gets to 8
if(count == 8) {
// for formatting
printf("%s","\n");
// Encrypt the eight characters and store them back in the char array.
//DES_encrypt1(eight_bits,&key,ENC);
doCBCenc((DES_LONG*)eight_bits, roundOutput, outFile);
#if 0
// prints out the encrypted string
int k;
for(k = 0; k < getSize(eight_bits); k++){
printf("%c", eight_bits[k]);
}
#endif
// Sets count back to 0 so that we can do another 8 characters.
count = 0;
// so we just do the first 8. When everything works REMOVE THE BREAK.
//break;
}
}
#if MAYBE
memset (eight_bits+count, 8-count, 8-count); // PKCS5/7 padding
doCBCenc((DES_LONG*)eight_bits, roundOutput, outFile);
#endif
} else {
printf("Error in opening file\n");
}
fclose(inpFile);
fclose(outFile);
#if 0
// End the timing
gettimeofday(&end, NULL);
// Initialize seconds and micros to hold values for the time output
long seconds = (end.tv_sec - start.tv_sec);
long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);
// Output the time
printf("The elapsed time is %d seconds and %d microseconds\n", seconds, micros);
#endif
}
PS: personally I wouldn't put the fwrite in doCBCenc; I would only do the encryption and let the caller do whatever I/O is appropriate which might in some cases not be fwrite. But what you have is not wrong for the requirements you apparently have.

how to create array of struct

I want to implement a searching table and
here's the data:
20130610 Diamond CoinMate 11.7246 15.7762 2897
20130412 Diamond Bithumb 0.209 0.2293 6128
20130610 OKCash Bithumb 0.183 0.2345 2096
20130412 Ethereum Chbtc 331.7282 401.486 136786
20170610 OKCash Tidex 0.0459 0.0519 66
...
and my code
typedef struct data{
int *date;
string currency[100];
string exchange[100];
double *low;
double *high;
int *daily_cap;
} Data;
int main()
{
FILE *fp = fopen("test_data.txt", "r");
Data tmp[50];
int i = 0;
while (!feof(fp)){
fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
i++;
}
fclose(fp);
}
but the first problem is that I can't create a large array to store my struct like
Data tmp[1000000]
and even I try just 50 elements , the program break down when finish main().
can anyone tell how to fix it or give me a better method, thanks.
You can not scan a value to an unallocated space, in other words, you need room for all those pointers in the struct, switch to
typedef struct data{
int date;
string currency[100];
string exchange[100];
double low;
double high;
int daily_cap;
} Data;
Or use malloc to assign space to those pointers before using them.
while (!feof(fp)){
tmp[i].date = malloc(sizeof(int));
...
But in this case, you don't need to pass the address of such members to fscanf since they are already pointers:
fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, ..
should be
fscanf(fp, "%d%s%s%lf%lf%7d", tmp[i].date, ...
Notice that double wants %lf instead of %f
This is also very confusing:
typedef struct data{
int *date;
string currency[100];
...
Is string a typedef of char? I think you mean string currency; since string is usually an alias of char *, in this case you need room for this member too: currency = malloc(100);
Finally, take a look to Why is “while ( !feof (file) )” always wrong?
There are too many errors in a short snippet, I suggest you to read a good C book.
Your code corrected using dynamic memory that allows you to reserve space for a big amount of data (see the other answer of #LuisColorado) and using fgets and sscanf instead of fscanf:
#include <stdio.h>
#include <stdlib.h>
typedef struct data{
int date;
char currency[100];
char exchange[100];
double low;
double high;
int daily_cap;
} Data;
int main(void)
{
FILE *fp = fopen("test_data.txt", "r");
/* Always check the result of fopen */
if (fp == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
Data *tmp;
tmp = malloc(sizeof(*tmp) * 50);
if (tmp == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
char buf[512];
int i = 0;
/* Check that you don't read more than 50 lines */
while ((i < 50) && (fgets(buf, sizeof buf, fp))) {
sscanf(buf, "%d%99s%99s%lf%lf%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
i++;
}
fclose(fp);
/* Always clean what you use */
free(tmp);
return 0;
}
Of course you can't. Think you are creating an array of 1.0E6 registers of sizeof (Data) which I guess is not less than 32 (four pointers) and 200 bytes (not less than this, as you don't give the definition of type string) and this is 232MBytes (at least) in a 64 byte machine (in 32bit it is 216MBytes) and that in case the type string is only one character wide (what I fear is not) In case string is a typedef of char * then you have 432 pointers in your struct giving to 432MBytes in only one variable. Next, if you are declaring this absolutely huge variable as a local variable, you must know that te stack in most unix operating systems is limited to around 8Mb, and this means you need to build your program with special parameters to allow a larger stack max size. And also you probably need your account to raise to that size also the ulimits to make the kernel to allow you such a large stack size segment.
Please, next time, give us full information, as not knowing the definition of the string type, or posting an incomplete program, only allows us to make guesses on what can be ongoing, and not to be able to discover actual errors. This makes you to waste your time, and for us the same. Thanks.
If your list of currency and exchange are known before hand, then there is no need to allocate or store any arrays within your struct. The lists can be global arrays of pointers to string literals and all you need do is store a pointer to the literal for both currency and exchange (you can even save a few more bytes by storing the index instead of a pointer).
For example, your lists of exchanges can be stored once as follows:
const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
*exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };
(if the number warrants, allocate storage for the strings and read them from a file)
Now you have all of the possible strings for currency and exchange stored, all you need in your data struct is a pointer for each, e.g.
typedef struct {
const char *currency, *exchange;
double low, high;
unsigned date, daily_cap;
} data_t;
(unsigned gives a better range and there are no negative dates or daily_cap)
Now simply declare an array of data_t (or allocate for them, depending on number). Below is a simply array of automatic storage for example purposes. E.g.
#define MAXD 128
...
data_t data[MAXD] = {{ .currency = NULL }};
Since you are reading 'lines' of data, fgets or POSIX getline are the line-oriented choices. After reading a line, you can parse the line with sscanf using temporary values, compare whether the values for currency and exchange read from the file match values stored, and then assign a pointer to the appropriate string to your struct, e.g.
int main (void) {
char buf[MAXC] = "";
size_t n = 0;
data_t data[MAXD] = {{ .currency = NULL }};
while (n < MAXD && fgets (buf, MAXC, stdin)) {
char curr[MAXE] = "", exch[MAXE] = "";
int havecurr = 0, haveexch = 0;
data_t tmp = { .currency = NULL };
if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date,
curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
for (int i = 0; i < NELEM(currency); i++) {
if (strcmp (currency[i], curr) == 0) {
tmp.currency = currency[i];
havecurr = 1;
break;
}
}
for (int i = 0; i < NELEM(exchange); i++) {
if (strcmp (exchange[i], exch) == 0) {
tmp.exchange = exchange[i];
haveexch = 1;
break;
}
}
if (havecurr & haveexch)
data[n++] = tmp;
}
}
...
Putting it altogether in a short example, you could do something similar to the following:
#include <stdio.h>
#include <string.h>
#define MAXC 256
#define MAXD 128
#define MAXE 32
#define NELEM(x) (int)(sizeof (x)/sizeof (*x))
const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
*exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };
typedef struct {
const char *currency, *exchange;
double low, high;
unsigned date, daily_cap;
} data_t;
int main (void) {
char buf[MAXC] = "";
size_t n = 0;
data_t data[MAXD] = {{ .currency = NULL }};
while (n < MAXD && fgets (buf, MAXC, stdin)) {
char curr[MAXE] = "", exch[MAXE] = "";
int havecurr = 0, haveexch = 0;
data_t tmp = { .currency = NULL };
if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date,
curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
for (int i = 0; i < NELEM(currency); i++) {
if (strcmp (currency[i], curr) == 0) {
tmp.currency = currency[i];
havecurr = 1;
break;
}
}
for (int i = 0; i < NELEM(exchange); i++) {
if (strcmp (exchange[i], exch) == 0) {
tmp.exchange = exchange[i];
haveexch = 1;
break;
}
}
if (havecurr & haveexch)
data[n++] = tmp;
}
}
for (size_t i = 0; i < n; i++)
printf ("%u %-10s %-10s %8.4f %8.4f %6u\n", data[i].date,
data[i].currency, data[i].exchange, data[i].low,
data[i].high, data[i].daily_cap);
}
Example Use/Output
$ ./bin/coinread <dat/coin.txt
20130610 Diamond CoinMate 11.7246 15.7762 2897
20130412 Diamond Bithumb 0.2090 0.2293 6128
20130610 OKCash Bithumb 0.1830 0.2345 2096
20130412 Ethereum Chbtc 331.7282 401.4860 136786
20170610 OKCash Tidex 0.0459 0.0519 66
With this approach, regardless whether you allocate for your array of struct or use automatic storage, you minimize the size of the data stored by not duplicating storage of known values. On x86_64, your data_t struct size will be approximately 40-bytes. With on average a 1-4 Megabyte stack, you can store a lot of 40-byte structs safely before you need to start allocating. You can always start with automatic storage, and if you reach some percentage of the available stack space, dynamically allocate, memcpy, set a flag to indicate the storage in use and keep going...

Fragmentation in custom allocator. Unable to order pointers correctly

Context: I have written a best-fit memory allocator. It allocates large blocks of memory, and serves best fitting chunks of it to programs upon request. If memory reserves are exhausted, its asks for OS for more. All free blocks are stored on a linked-list, ordered by increasing pointer value.
Problem:: When memory is released, the program is supposed to link it back into the free list of blocks for recycling, and more crucially merges the given block with its surrounding blocks if possible. Unfortunately, this only works well as long as I do not need to ask the OS for more then one super-block to serve. When this does occur, the new blocks I receive have nonsensical addressing and get inserted between other super-block addressing space. This leads to permanent fragmentation.
Tl;dr: I am being given new super-blocks of memory whose address is within the address space of another super-block, leading to fragmentation when sub-blocks are returned.
Illustration of problem:
Here is a diagram of the problem I described above.
Numbers: Memory addresses (from real execution).
Beige blocks: Free memory.
White blocks: Memory unlinked for use.
The diagram shows the progression of memory usage until the fragmentation catalyst occurs. You can see once the block gets inserted, merging will never be possible up for the busy blocks once they are checked back in.
Code: Reproducible Example:
The following includes the allocator and a small test program. It must be compiled with at least C99.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/*****************************************************************************/
/* SYMBOLIC CONSTANTS */
/*****************************************************************************/
#define NEXT(hp) ((hp)->key.next)
#define UNITS(hp) ((hp)->key.units)
#define UNIT_SIZE sizeof(Header)
#define MIN_UNITS_ALLOC 64
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/*****************************************************************************/
/* TYPE DEFINITIONS */
/*****************************************************************************/
typedef union header {
intmax_t align;
struct {
union header *next;
unsigned units;
} key;
} Header;
/*****************************************************************************/
/* GLOBAL VARIABLES */
/*****************************************************************************/
Header base = {.key = { NULL, 0 }};
Header *list;
/*****************************************************************************/
/* PROTOTYPES */
/*****************************************************************************/
/* Allocates a 'bytes' size block of memory. On success, returns pointer to
* the block. On error, NULL is returned. */
void *alloc (size_t bytes);
/* Returns a 'bytes' size block of allocated memory for reuse */
void release (void *bytes);
/* Attempts to reserve memory from the operating system via a system call */
static Header *reserve (unsigned units);
/*****************************************************************************/
/* FUNCTION IMPLEMENTATIONS */
/*****************************************************************************/
void *alloc (size_t bytes) {
size_t units, diff;
Header *block, *lastBlock, *best, *lastBest;
if (bytes == 0) {
return NULL;
}
if (list == NULL) {
list = base.key.next = &base;
}
best = lastBest = NULL;
units = (bytes + UNIT_SIZE - 1) / UNIT_SIZE + 1;
diff = SIZE_MAX;
for (lastBlock = list, block = NEXT(list); ; lastBlock = block, block = NEXT(block)) {
/* Loop across list, find closest fitting block */
if (UNITS(block) >= units && UNITS(block) - units < diff) {
diff = UNITS(block) - units;
best = block;
lastBest = lastBlock;
}
/* Upon cycle completion */
if (block == list) {
/* If no block available, reserve some. */
if (best == NULL) {
if ((lastBest = reserve(units)) == NULL) {
return NULL;
} else {
fprintf(stderr, "\nalloc: Out of memory, linking new block %lld of size %u.\n\n", (long long)lastBest, UNITS(lastBest));
release((void *)(lastBest + 1));
}
/* If block of perfect size, return. Else slice and return */
} else {
if (diff == 0) {
NEXT(lastBest) = NEXT(best);
} else {
UNITS(best) = diff;
best += diff;
UNITS(best) = units;
}
fprintf(stderr, "alloc: Unlinked block %lld of %u units.\n", (long long)best, UNITS(best));
return (void *)(best + 1);
}
}
}
}
void release (void *bytes) {
Header *p, *block;
block = (Header *)bytes - 1;
/* Choose p such that: p -> block -> NEXT(p) */
for (p = list; !(p < block && NEXT(p) > block); p = NEXT(p)) {
if (p >= NEXT(p) && (block > p || block < NEXT(p))) {
break;
}
}
/* Merge block with NEXT(p) if adjacent */
if (block + UNITS(block) == NEXT(p)) {
NEXT(block) = NEXT(NEXT(p));
UNITS(block) += UNITS(NEXT(p));
} else {
NEXT(block) = NEXT(p);
}
/* Merge block with p if adjacent */
if (p + UNITS(p) == block) {
NEXT(p) = NEXT(block);
UNITS(p) += UNITS(block);
} else {
NEXT(p) = block;
}
}
static Header *reserve (unsigned units) {
char *bytes, *sbrk(int);
Header *block;
units = MAX(units, MIN_UNITS_ALLOC);
if ((bytes = sbrk(units)) == (char *)-1) {
return NULL;
} else {
block = (Header *)bytes;
UNITS(block) = units;
}
return block;
}
/*****************************************************************************/
/* TESTING FUNCTIONS (DELETE) */
/*****************************************************************************/
void printFreeList (unsigned byAddress) {
Header *lp = list;
if (lp == NULL) {
fprintf(stdout, "List is NULL\n");
return;
}
do {
fprintf(stdout, "[ %lld ] -> ", (byAddress ? (long long)lp : (long long)UNITS(lp)));
lp = NEXT(lp);
} while (lp != list);
putc('\n', stdout);
}
void releaseItemAtIndex (int i, int k, long long *p[]) {
fprintf(stderr, "Releasing block %d/%d\n", i, k);
release(p[i]);
for (int j = i; j < k; j++) {
if (j + 1 < k) {
p[j] = p[j + 1];
}
}
}
#define MAX_TEST_SIZE 5000
int main (int argc, const char *argv[]) {
/* Seed PRNG: For removed random deletion (now manual) */
srand(time(NULL));
/* Array of pointers to store allocated blocks, blockSize we want to allocate. */
long long *p[MAX_TEST_SIZE], blockSize = 256;
/* Number of blocks we choose to allocate */
int k = 6;
/* Allocate said blocks */
for (int i = 0; i < k; i++) {
p[i] = alloc(blockSize * sizeof(char));
printFreeList(0); printFreeList(1); putchar('\n');
}
fprintf(stderr, "\n\n");
int idx;
while (k > 0) {
fprintf(stderr, "Delete an index between 0 up to and including %d:\n", k - 1);
scanf("%d", &idx);
releaseItemAtIndex(idx, k, p);
printFreeList(0); printFreeList(1); putchar('\n');
k--;
}
return 0;
}
Miscellaneous Details:
I am running a 64 bit operating system.
I do not know if pointer comparison on the heap is guaranteed to be valid. This is not guaranteed by the standard according to K&R.
Well I eventually wrote one a couple years later that seems to work alright. Bonus: It's using static memory.
Header File
#if !defined(STATIC_ALLOCATOR_H)
#define STATIC_ALLOCATOR_H
/*
*******************************************************************************
* (C) Copyright 2020 *
* Created: 07/04/2020 *
* *
* Programmer(s): *
* - Jillian Oduber *
* - Charles Randolph *
* *
* Description: *
* Static first-fit memory allocator *
* *
*******************************************************************************
*/
#include <iostream>
#include <cstddef>
#include <cstdint>
#include "dx_types.h"
extern "C" {
#include "stddef.h"
}
/*
*******************************************************************************
* Type Definitions *
*******************************************************************************
*/
// Defines the header block used in the custom allocator
typedef union block_h {
struct {
union block_h *next;
size_t size; // Size is in units of sizeof(block_h)
} d;
max_align_t align;
} block_h;
/*
*******************************************************************************
* Class Definitions *
*******************************************************************************
*/
class Static_Allocator {
public:
/*\
* #brief Configures the class with the given static memory capacity
* #param memory Pointer to static array (must support address comparison)
* #param size Total number of bytes alloted for distribution
\*/
Static_Allocator(uint8_t *memory, size_t size);
/*\
* #brief Returns a memory block of the desired size
* #param size Number of bytes to allocate
* #return
* - valid pointer if memory is available
* - NULL otherwise
\*/
uint8_t *alloc (size_t size);
/*\
* #brief Releases the allocated block of memory
* #param ptr Pointer to allocated block of memory
* #return
* - STATUS_OK on success
* - STATUS_ERR if invalid block
* - STATUS_BAD_PARAM on NULL parameter
\*/
dx::status_t free (uint8_t *ptr);
/*\
* #brief Returns the amount of free memory
* #return size_t Bytes (of available free memory)
\*/
size_t free_memory_size ();
/*\
* #brief Debug utility which shows what is on the free-list
* #return None
\*/
void show ();
/*\
* #brief Debug utility which asserts there is only a single
* memory block
\*/
bool unified ();
private:
// Fixed allocator capacity
size_t d_capacity;
// Fixed memory
uint8_t *d_memory;
// Pointer to head of the linked list
block_h *d_free_list;
// Available memory
size_t d_free_memory_size;
// Unit size
static const size_t mem_unit_size = sizeof(block_h);
};
#endif
Source File
#include "static_allocator.h"
Static_Allocator::Static_Allocator (uint8_t *memory, size_t size):
d_capacity(0),
d_memory(NULL),
d_free_list(NULL),
d_free_memory_size(0)
{
// Assign memory array
d_memory = memory;
// Trim off two mem_unit_size for head + init-block + spillover
d_capacity = size - (size % mem_unit_size) - 2 * mem_unit_size;
// Ensure free memory is set to capacity
d_free_memory_size = d_capacity;
}
uint8_t * Static_Allocator::alloc (size_t size)
{
block_h *last, *curr;
// Number of blocks sized units to allocate
size_t nblocks = (size + mem_unit_size - 1) / mem_unit_size + 1;
// If larger than total capacity or invalid size; immediately return
if ((nblocks * sizeof(block_h)) > d_capacity || size == 0) {
return NULL;
}
// If uninitialized, create initial list units
if ((last = d_free_list) == NULL) {
block_h *head = reinterpret_cast<block_h *>(d_memory);
head->d.size = 0;
block_h *init = head + 1;
init->d.size = d_capacity / sizeof(block_h);
init->d.next = d_free_list = last = head;
head->d.next = init;
}
// Problem: You must not be allowed to merge the init block
// Look for space - stop if wrapped around
for (curr = last->d.next; ; last = curr, curr = curr->d.next) {
// If sufficient space available
if (curr->d.size >= nblocks) {
if (curr->d.size == nblocks) {
last->d.next = curr->d.next;
} else {
curr->d.size -= nblocks;
curr += curr->d.size; // Suspect
curr->d.size = nblocks;
}
// Reassign free list head
d_free_list = last;
// Update amount of free memory available
d_free_memory_size -= nblocks * sizeof(block_h);
return reinterpret_cast<uint8_t *>(curr + 1);
}
// Otherwise not sufficient space. If reached
// the head of the list again, there is no more
// memory left
if (curr == d_free_list) {
return NULL;
}
}
}
dx::status_t Static_Allocator::free (uint8_t *ptr)
{
block_h *b, *p;
// Check if parameter is valid
if (ptr == NULL) {
std::cerr << "Null pointer!";
return dx::STATUS_BAD_PARAM;
}
// Check if memory is in range
if (!(ptr >= (d_memory + 2 * mem_unit_size)
&& ptr < (d_memory + d_capacity + 2 * mem_unit_size))) {
std::cerr << "Pointer out of range!";
return dx::STATUS_ERR;
}
// Obtain block header (ptr - sizeof(block_h))
b = reinterpret_cast<block_h *>(ptr) - 1;
// Update available memory size
d_free_memory_size += b->d.size;
// Find insertion location for block
for (p = d_free_list; !(b >= p && b < p->d.next); p = p->d.next) {
// If the block comes at the end of the list - break
if (p >= p->d.next && b > p) {
break;
}
// If at the end of the list, but block comes before next link
if (p >= p->d.next && b < p->d.next) {
break;
}
}
// [p] <----b----> [p->next] ----- [X]
// Check if we can merge forwards
if (b + b->d.size == p->d.next) {
b->d.size += (p->d.next)->d.size;
b->d.next = (p->d.next)->d.next;
} else {
b->d.next = p->d.next;
}
// Check if we can merge backwards
if (p + p->d.size == b) {
p->d.size += b->d.size;
p->d.next = b->d.next;
} else {
p->d.next = b;
}
d_free_list = p;
return dx::STATUS_OK;
}
size_t Static_Allocator::free_memory_size ()
{
return d_free_memory_size;
}
void Static_Allocator::show ()
{
std::cout << "Block Size: " << sizeof(block_h) << "\n"
<< "Capacity: " << d_capacity << "\n"
<< "Free Size (B): " << d_free_memory_size << "\n";
if (d_free_list == NULL) {
std::cout << "<uninitialized>\n";
return;
}
// Show all blocks
block_h *p = d_free_list;
do {
std::cout << "-------------------------------\n";
std::cout << "Block Address: " << (uint64_t)(p) << "\n";
std::cout << "Blocks (32B): " << p->d.size << "\n";
std::cout << "Next: " << (uint64_t)(p->d.next) << "\n";
std::cout << "{The next block is " << (uint64_t)((p->d.next) - (p)) << " bytes away, but we claim the next " << p->d.size * mem_unit_size << " bytes\n";
p = p->d.next;
} while (p != d_free_list);
std::cout << "-------------------------------\n\n\n";
}
bool Static_Allocator::unified () {
if (d_free_list == NULL) {
return false;
}
return ((d_free_list->d.next)->d.next == d_free_list);
}

Loss of values in array in struct after function execution

I am working on a c code that holds a structure that hosts some values which I call range.
My purpose is to use this so called range dynamically (holding different amount of data at every execution). I am now provisionally using the # define comp instead. This so called range gets updated every time I call my update_range though the use of s1 structure (and memory allocations).
What I found weird is that when I introduced a "show_range" function to output the actual values inside/outside the update function I realized that I loose the first two values.
Here is the code.
Any suggestions on that?
Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <complex.h>
#define comp 1024
// struct holding a complex-valued range
struct range {
int dimensions; /* number of dimensions */
int* size; /* array holding number of points per dimension */
complex double* values; /* array holding complex valued */
int components; /* number of components that will change on any execution*/
};
// parameters to use in function
struct s1 {
int tag;
struct range* range;
};
int update_range(struct s1* arg);
int show_range(struct range* argrange, char* message);
int copy_range(struct range* in, struct range* out);
int main(void) {
int ret = 0;
struct s1 s1;
s1.tag = 0;
s1.range = malloc(sizeof(struct range));
update_range(&s1);
show_range(s1.range, "s1.range inside main function");
return ret;
}
////////////////////////////////////////////
int update_range(struct s1* arg) {
int ret = 0;
int i;
struct range range;
range.dimensions = 1;
range.size = malloc(range.dimensions * sizeof(int));
range.components = comp;
range.size[0] = range.components; // unidimensional case
range.values = malloc(range.components * sizeof(complex double));
for (i = 0; i < range.components; i++) {
range.values[i] = (i + 1) + I * (i + 1);
}
show_range(&range, "range inside update_range function");
arg->range->size =
malloc(range.dimensions * sizeof(int)); // size was unknown before
arg->range->values =
malloc(comp * sizeof(complex double)); // amount of values was unknown
copy_range(&range, arg->range);
show_range(arg->range, "arg->range inside update_range function");
if (range.size)
free(range.size);
range.size = NULL;
if (range.values)
free(range.values);
range.values = NULL;
return ret;
}
////////////////////////////////////////////
// Show parameters (10 first values)
int show_range(struct range* argrange, char* message) {
int ret = 0;
vint i;
printf(" ******************************\n");
printf(" range in %s \n", message);
printf(" arg.dimensions=%d \n", argrange->dimensions);
printf(" arg.size[0]=%d \n", argrange->size[0]);
printf(" argrange.components=%d \n", argrange->components);
printf(" first 10 {Re} values: \n");
for (i = 0; i < 10; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
printf("\n");
return ret;
}
////////////////////////////////////////////
// copy range
int copy_range(struct range* in, struct range* out) {
int ret = 0;
if (in == NULL) {
fprintf(stderr, "error: in points to NULL (%s:%d)\n", __FILE__,
__LINE__);
ret = -1;
goto cleanup;
}
if (out == NULL) {
fprintf(stderr, "error: out points to NULL (%s:%d)\n", __FILE__,
__LINE__);
ret = -1;
goto cleanup;
}
out->dimensions = in->dimensions;
out->size = in->size;
out->values = in->values;
out->components = in->components;
cleanup:
return ret;
}
Your copy_range function is broken, because it copy only pointer to size and values and not the memory. After you call free(range.size); and free(range.values); you are deleting mamory also from original object but without setting its pointers back to NULL.
After calling update_range, s1.range has non NULL pointers in size and values, but they are pointing to deleted memory.
You are experiencing undefined behaviour (UB) due to accessing freed memory. Your copy_range() function only does a shallow copy of the two pointer fields so when you run free(range->size) you make arg->range->size invalid.
You should make copy_range() a deep copy by allocating and copying the pointer contents like:
out->size = malloc(in->dimensions * sizeof(int));
memcpy(out->size, in->size, in->dimensions * sizeof(int));
out->values = malloc(in->components * sizeof(complex double));
memcpy(out->values , in->values, in->components * sizeof(complex double));
There are not 10 items to print, so the lines:
printf(" first 10 {Re} values: \n");
for (i = 0; i < 10; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
Will be printing from random memory.
a much better method would be:
printf(" first %d {Re} values: \n", min(argrange.components,10));
for (i = 0; i < argrange.components; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
The above is just one of many problems with the code.
I would suggest executing the code using a debugger to get the full story.
as it is, the code has some massive memory leaks due mostly
to overlaying malloc'd memory pointers.
for instance as in the following:
arg->range->size =
malloc(range.dimensions * sizeof(int)); // size was unknown before
arg->range->values =
malloc(comp * sizeof(complex double)); // amount of values was unknown

Hash Table Project in C ... dereferencing pointer to incomplete type errors [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I seem to be going in circles with this project! I get so many errors of 'dereferencing pointer to incomplete type', and have quite a few others. it seems like when I fix one another one will pop up to take its place!
It's my first time using hash tables, and I admit that I am rather lost but I think I made a very good start at least. Any input as to how to solve my 'dereferencing pointer to incomplete type' problems would be amazing!
htable.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "htable.h"
struct htablerec {
int size;
int num_entries;
hashing_t method;
char **keys;
int *freqs;
int *stats;
};
void *emalloc(size_t s) {
void *result = malloc(s);
if (NULL == result) {
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/*moves pointer to point to something appropriate*/
htable htable_new(int capacity) {
int i;
htable ht = emalloc(sizeof *ht);
ht->size = size;
ht->method = method;
ht->num_keys = 0;
ht->keys = emalloc(size * sizeof ht->keys[0]);
ht->freqs = emalloc(size * sizeof ht->freqs[0]);
ht->stats = emalloc(size * sizeof ht->stats[0]);
for(i = 0; i<size; i++){
ht->keys[i] = NULL;
ht->freqs[i] = 0;
ht->stats[i] = 0;
}
return ht;
}
static unsigned int htable_step(htable h, unsigned int i_key){
return 1 + (i_key % (h->size - 1));
}
static unsigned int htable_wtoi(char *word){
unsigned int result = 0;
while(*word != '\0') result = (*word++ +31 * result);
return result;
}
static unsigned int htable_hash(htable h, unsigned int i_key){
return i_key % h->size;
}
void htable_free(htable h) {
int i;
for(i = 0; i<h->size; i++){
if(h->keys[i] != NULL){
free(h->keys[i]);
}
}
if(h->keys != NULL){
free(h->keys);
}
if(h->freqs != NULL){
free(h->freqs);
}
free(h);
}
static unsigned int htable_word_to_int(char *word) {
unsigned int result = 0;
while (*word != '\0') {
result = (*word++ + 31 * result);
}
return result;
}
int htable_insert(htable h, char *str) {
int num_collisions = 0;
int i_key = htable_wtoi(key);
int pos = htable_hash(h, i_key);
int step = 1;
if(h->method == DOUBLE)
step = htable_step(h, i_key);
while(h->keys[pos]!=NULL &&
strcmp(h->keys[pos],key)!=0 &&
num_collisions < h->size ){
pos = htable_hash(h, pos + step);
num_collisions++;
}
if(h->keys[pos] == NULL){
h->keys[pos] = emalloc((strlen(key)+1) * sizeof h->keys[0][0]);
strcpy(h->keys[pos],key);
h->stats[h->num_keys] = num_collisions;
h->num_keys++;
}
if(num_collisions >= h->size) /* We must be full, so return zero.*/
return 0;
return ++(h->freqs[pos]);
}
static int htable_search(htable h, char *key){
int num_collisions = 0;
int i_key = htable_wtoi(key);
int pos = htable_hash(h, i_key);
int step = 1;
if(h->method == DOUBLE)
step = htable_step(h, i_key);
while(h->keys[pos]!=NULL &&
strcmp(h->keys[pos],key)!=0 &&
num_collisions < h->size ){
pos = htable_hash(h, pos + step);
num_keys++;
}
if(num_keys >= h->size)
return 0;
else
return h->freqs[pos];
}
void htable_print(htable h, FILE *stream){
int i;
for(i = 0; i<h->size; i++){
if(h->keys[i] != NULL)
fprintf(stream, "%d\t%s\n",i, h->freqs[i], h->keys[i]);
}
}
void htable_print_entire_table(htable h, FILE *stream) {
int i;
for (i=0; loop < h->capacity; i++) {
if (h->key[i] != NULL) {
fprintf("%d\t%s\n", h->freqs[i], h->key[i]);
}
}
}
/**
* Prints a line of data indicating the state of the hash table when
* it is a given percentage full.
*
* The data is printed out right justified (with the given field widths,
* and decimal places) in this order:
*
* - How full the hash-table is as a percentage (4)
* - How many keys are in the hash-table at that point (11)
* - What percentage of those keys were placed 'at home' (12, 1 dp)
* - The average number of collisions per key placed (12, 2 dp)
* - The maximum number of collisions while placing a key (12)
*
* #param h the hash-table to get data from.
* #param stream the place to send output to.
* #param percent_full the point at which to print the statistics.
* If the hashtable is less full than that, then
* nothing will be printed.
*/
static void print_stats_line(htable h, FILE *stream, int percent_full) {
int current_entries = h->capacity * percent_full / 100;
double average_collisions = 0.0;
int at_home = 0;
int max_collisions = 0;
int i = 0;
if (current_entries > 0 && current_entries <= h->num_keys) {
for (i = 0; i < current_entries; i++) {
if (h->stats[i] == 0) {
at_home++;
}
if (h->stats[i] > max_collisions) {
max_collisions = h->stats[i];
}
average_collisions += h->stats[i];
}
fprintf(stream, "%4d%11d%12.1f%12.2f%12d\n", percent_full,
current_entries, at_home * 100.0 / current_entries,
average_collisions / current_entries, max_collisions);
}
}
void htable_print_stats(htable ht, FILE *stream, int num_stats) {
int i;
fprintf(stream, "Percent Current Percent Average Maximum\n");
fprintf(stream, " Full Entries At Home Collisions Collisions\n");
fprintf(stream, "-----------------------------------------------------\n");
for (i = 1; i <= num_stats; i++) {
print_stats_line(ht, stream, 100 * i / num_stats);
}
fprintf(stream, "-----------------------------------------------------\n\n");
}
htable.h
#ifndef HTABLE_H
#define HTABLE_H
#include <stdio.h>
typedef struct hashtable *htable;
typedef enum hashing_e { LINEAR, DOUBLE } hashing_t;
extern htable htable_new(int size);
extern void htable_destroy(htable ht);
extern int htable_insert(htable h, char *key);
extern int htable_search(htable h, char *key);
extern void htable_print(htable h, FILE *stream);
extern void htable_print_stats(htable ht, FILE *stream, int num_stats);
#endif
main.c (given by tutor for project to fit)
/**
* #file main.c
* #author Iain Hewson
* #date August 2012
*
* This program is written to test the hash table ADT specified in
* Cosc242 assignment two. It creates a hash table which can use
* linear-probing or double-hashing as a collision resolution
* strategy. Various options are provided which make it possible to
* examine a hash table as well as see how it performs while being
* filled.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "mylib.h"
#include "htable.h"
/* A boolean type which can be TRUE or FALSE */
typedef enum bool_e {FALSE, TRUE} bool_t;
/* function declarations */
static void usage(char *progname);
static void setup(int argc, char **argv, bool_t *double_hashing,
bool_t *entire_table, bool_t *print_stats,
int *snapshots, int *tablesize);
/**
*
* Creates a hash-table and inserts words into it read from stdin.
* Arguments on the command line alter the behaviour of the program
* as follows:
* - -d Use double hashing (linear probing is the default)
* - -e Display entire contents of hash-table on stderr
* - -n NUM Show NUM statistics snapshots (if -p is used)
* - -p Print stats info instead of frequencies & words
* - -s SIZE Use the first prime >= SIZE as htable size
* - -h Display this message
*
* By default each word and it's frequency are printed to stdout.
*
* #param argc the number of command-line arguments.
* #param argv an array of strings containing the command-line arguments.
*
* #return EXIT_SUCCESS if the program is successful.
*/
int main(int argc,char **argv) {
bool_t entire_table = FALSE, double_hashing = FALSE, print_stats = FALSE;
int tablesize = 0, snapshots = 0;
char word[256];
htable ht;
setup(argc, argv, &double_hashing, &entire_table, &print_stats,
&snapshots, &tablesize);
ht = htable_new(tablesize, (double_hashing) ? DOUBLE_H : LINEAR_P);
while (getword(word, sizeof word, stdin) != EOF) {
htable_insert(ht, word);
}
if (entire_table) {
htable_print_entire_table(ht, stderr);
}
if (print_stats) {
htable_print_stats(ht, stdout, snapshots);
} else {
htable_print(ht, stdout); /* print words and frequencies */
}
htable_free(ht);
return EXIT_SUCCESS;
}
/**
* Prints out a usage message to stderr outlining all of the options.
* #param prog_name the name of the program to include in usage message.
*/
static void usage(char *prog_name) {
fprintf(stderr, "Usage: %s [OPTION]... <STDIN>\n\n%s%s", prog_name,
"Perform various operations using a hash-table. By default read\n"
"words from stdin and print them with their frequencies to stdout.\n\n"
" -d Use double hashing (linear probing is the default)\n"
" -e Display entire contents of hash-table on stderr\n",
" -n NUM Show NUM stats snapshots (if -p is used)\n"
" -p Print stats info instead of frequencies & words\n"
" -s SIZE Use the first prime >= SIZE as htable size\n\n"
" -h Display this message\n\n");
}
/**
* Handle options given on the command-line by setting a number of
* variables appropriately. May call usage() if incorrect arguments
* or -h given.
*
* #param argc the number of command-line arguments.
* #param argv an array of strings contain the command-line arguments.
* #param double_hashing set to TRUE if -d given
* #param entire_table set to TRUE if -e given
* #param snapshots set to NUM if -n NUM given and NUM > 0 else set to 10
* #param print_stats set to TRUE if -p given
* #param tablesize set to SIZE if -t SIZE given and SIZE > 0 else set to 113
*/
static void setup(int argc, char **argv, bool_t *double_hashing,
bool_t *entire_table, bool_t *print_stats,
int *snapshots, int *tablesize) {
const char *optstring = "dehpn:s:";
char option;
while ((option = getopt(argc, argv, optstring)) != EOF) {
switch (option) {
case 'd':
*double_hashing = TRUE;
break;
case 'e':
*entire_table = TRUE;
break;
case 'p':
*print_stats = TRUE;
break;
case 'n':
*snapshots = atoi(optarg);
break;
case 's':
*tablesize = atoi(optarg);
break;
case 'h':
default:
usage(argv[0]);
exit(EXIT_SUCCESS);
}
}
/* set default values if nothing sensible entered */
if (*tablesize < 1) *tablesize = 113;
if (*snapshots < 1) *snapshots = 10;
}
mylib.c
#include <stdlib.h>
#include "mylib.h"
/**************************
* *
* emalloc *
* *
**************************
Used to handle new memory allocation to a pointer and handle exceptions
which may arrise if memory allocation fails, which happens all the time
when you try and enter negative values.
PARAMETERS: s = calculated size of required memory.
RETURN VALUE: a pointer of any type.
*/
void *emalloc(size_t s){
void *result = malloc(s);
if(NULL == result){
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/**************************
* *
* erealloc *
* *
**************************
Handles the reallocation of an updated amount of memory to an existing
with existing data attached.
PARAMETERS: p = the existing pointer we would like additional memory
allocated to.
s = calculated size of required memory.
RETURN VALUE: a pointer of any type.
*/
void *erealloc(void *p, size_t s){
void *result = realloc(p,s);
if(NULL == result){
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/**************************
* *
* getword *
* *
**************************
Is used to read input from the designated file stream (standard in for the
assignment). Getword removes white space with the first while loop. And only
returns one word. Maintaining continous input is therefore the responsibility
of the calling function.
PARAMETERS: s = the pointer to the character array.
limit = the maximum size a word can be.
stream = where to read from.
RETURN VALUE: the integer value of the character at s[0]. The string s does
not need to be returned, since it is an array and is passed as
a memory address. If no chars were read into the string s, then
s[0] would have the '\0' [NULL] value which equates to 0 if used
in a boolean equation.
*/
int getword(char *s, int limit, FILE *stream){
int c;
while(!isalnum( c = getc(stream)) && c != EOF);
if(c == EOF)
return EOF;
else
*s++ = tolower(c);
while(--limit > 0){
if(isalnum(c = getc(stream)))
*s++ = tolower(c);
else if('\'' == c) continue;
else break;
}
*s = '\0';
return s[0];
}
/**************************
* *
* stoi *
* *
**************************
Not using this function now.
PARAMETERS: s = string representation of a number.
RETURN VALUE: the integer value.
*/
int stoi(char *s){
int i,j,r;
int sign = 1;
i=1;
r=0;
for(j=my_strlen(s)-1; j>=0; j--){
if(j == 0 && s[j] == '-')
sign = -1;
else {
if(s[j]>='0' && s[j]<='9'){
r+=((s[j]-'0')*i);
i*=10;
} else {
fprintf(stderr, "Invalid input for String to int\n");
exit(EXIT_FAILURE);
}
}
}
return r * sign;
}
/**************************
* *
* my_strlen *
* *
**************************
I am using my own string length function, but I wrote it with the stoi
function, so am using it here. Perhaps not as safe as the library
functions?
PARAMETERS: s = a string delimited by the NULL character.
RETURN VALUE: the number of characters in the string.
*/
int my_strlen(char *s){
int i=0;
while(s[i]!='\0')
i++;
return i;
}
/**************************
* *
* factors *
* *
**************************
Another unrequired function used to calculate the possiblity of factorisation.
PARAMETERS: x = An integer to be factored towards.
RETURN VALUE: 0 if x has factors, 1 if x is a prime number.
*/
static int factors(int x){
int f = 2;
while(f*f < x){
if(x % f == 0){
return 0;
} else {
f++;
}
}
return 1;
}
/**************************
* *
* prime_gt *
* *
**************************
Used in conjunction with factors to find factorless integers. We increment
bound until it is truely prime.
Bound - We start with bound, sending it to the factors function. If it is
a prime number, then stop searching. Otherwise loop until we find
an prime integer larger than the input integer.
PARAMETERS: s = the input integer.
RETURN VALUE: Bound, for it is now a prime number.
*/
static int prime_gt(int s){
int bound = s;
while(bound > 0){
if(factors(bound))
break;
else
bound++;
}
return bound;
}
/**************************
* *
* relative_prime *
* *
**************************
Decides on a prime number to use to set the table size to.
PARAMETERS: s = the required size of the table.
RETURN VALUE: the newer beter prime number size for the table.
*/
unsigned int relative_prime(int s){
return prime_gt(s);
}
Sorry for it being so big, it's ok if it's just a complete unfixable jumble.
You don't seem to have defined struct hashtable anywhere. You need to say somewhere what fields that struct should actually contain, at the moment there is only a forward declaration in htable.h.
Such a forward declaration just says that the type exists, but not how it exactly looks like. Therefore it is considered an incomplete type, until the compiler sees a full definition of it.
Cause the definition of struct hashtable does not exist, the following line creates an alias of an incomplete pointer type (htable).
typedef struct hashtable *htable;
It is impossible to deference such pointer (the compiler doesn't know the different fields of your structure).
In your file htable.c, maybe you meant :
/* instead of struct htablered */
struct htable {
int size;
int num_entries;
hashing_t method;
char **keys;
int *freqs;
int *stats;
};

Resources