/* * File Name: data.c * Author: Jade Cheng * Date: March 29, 2009 * Course: ICS 451 * Assignment: Project 2 */ #include "common.h" #include "data.h" #include "timehelp.h" /** The number of seconds before a request times out. */ #define REQUEST_TIMEOUT 5 static size_t find_oldest(const timeval * time, size_t count); static int find_peer(const peers_t * peers, const peer_t * peer); /* ------------------------------------------------------------------------ */ extern void content_dir_add( content_dir_t * dst, const char * name, const peer_t * peer) { size_t index; assert(dst != NULL); assert(name != NULL); assert(peer != NULL); assert(dst->count <= MAX_CONTENT_DIR_ITEMS); assert(verify_name(name) == EXIT_SUCCESS); /* Return if the name and peer is found. */ for (index = 0; index < dst->count; index++) if (strcmp(dst->items[index].name, name) == 0) if (peer_cmp(&dst->items[index].peer, peer) == 0) return; /* Put the item at the end of the array if there is room, or else replace * the oldest item. */ if (dst->count < MAX_CONTENT_DIR_ITEMS) index = dst->count++; else index = find_oldest(dst->time, dst->count); /* Store the item, and set or reset the time. */ strcpy(dst->items[index].name, name); dst->items[index].peer = *peer; dst->time[index] = time_now(); } /* ------------------------------------------------------------------------ */ extern int content_dir_find( content_dir_t * src, const char * name, size_t * index) { assert(src != NULL); assert(name != NULL); assert(index != NULL); assert(verify_name(name) == EXIT_SUCCESS); /* Do not necessarily start from the first item. */ while (*index < src->count) { /* Reset the time if the item is found and return. */ if (strcmp(src->items[*index].name, name) == 0) { src->time[*index] = time_now(); return EXIT_SUCCESS; } (*index)++; } return EXIT_FAILURE; } /* ------------------------------------------------------------------------ */ extern void content_dir_print(const content_dir_t * src) { size_t i; assert(src != NULL); printf("Content Directory\n"); printf("----------------------------------------\n"); if (src->count == 0) { printf("Empty\n"); return; } for (i = 0; i < src->count; i++) { char ip[16]; u_short port; inet_ntop(AF_INET, &src->items[i].peer.addr, ip, sizeof(ip)); port = src->items[i].peer.port; printf("[%u] %16s:%-5hu %-20s %lu.%lu\n", i + 1, ip, port, src->items[i].name, src->time[i].tv_sec, src->time[i].tv_usec); } } /* ------------------------------------------------------------------------ */ extern void content_dir_remove(content_dir_t * dst, const char * name) { size_t index; assert(dst != NULL); assert(name != NULL); assert(verify_name(name) == EXIT_SUCCESS); /* Loop over every item in the content directory. */ index = 0; while (index < dst->count) { /* Skip this item if it does not match the name. */ if (strcmp(dst->items[index].name, name) != 0) { index++; continue; } /* Either remove the last item or replace the item with the last. */ if (index != --dst->count) dst->items[index] = dst->items[dst->count]; /* Clear the last item since so things look good in the debugger. */ memset(&dst->items[dst->count], 0, sizeof(content_dir_item_t)); } } /* ------------------------------------------------------------------------ */ extern void data_cache_add( data_cache_t * dst, const char * name, const u_char * buffer, size_t size) { size_t index; assert(dst != NULL); assert(name != NULL); assert(buffer != NULL); assert(dst->count <= MAX_DATA_CACHE_ITEMS); assert(verify_name(name) == EXIT_SUCCESS); assert(size > 0); assert(size <= MAX_CONTENT_LENGTH); /* This will update the time if it is found. */ if (EXIT_SUCCESS == data_cache_find(dst, name, &index)) return; /* Put the item at the end of the array if there is room, or else replace * the oldest item. */ if (dst->count < MAX_DATA_CACHE_ITEMS) index = dst->count++; else index = find_oldest(dst->time, dst->count); /* Store the item, and set or reset the time. */ memcpy(dst->items[index].buffer, buffer, size); strcpy(dst->items[index].name, name); dst->items[index].size = size; dst->time[index] = time_now(); } /* ------------------------------------------------------------------------ */ extern int data_cache_find( data_cache_t * src, const char * name, size_t * index) { assert(src != NULL); assert(name != NULL); assert(index != NULL); assert(verify_name(name) == EXIT_SUCCESS); /* Search for the item, and reset its time if it is found. */ for (*index = 0; *index < src->count; (*index)++) { if (strcmp(src->items[*index].name, name) == 0) { src->time[*index] = time_now(); return EXIT_SUCCESS; } } return EXIT_FAILURE; } /* ------------------------------------------------------------------------ */ extern void data_cache_print(const data_cache_t * src) { size_t i; assert(src != NULL); printf("Data Cache\n"); printf("----------------------------------------\n"); if (src->count == 0) { printf("Empty\n"); return; } for (i = 0; i < src->count; i++) printf("[%u] %-20s %lu.%lu\n", i + 1, src->items[i].name, src->time[i].tv_sec, src->time[i].tv_usec); } /* ------------------------------------------------------------------------ */ extern int local_content_add(local_content_t * dst, const char * name) { FILE * f; size_t index; size_t size; assert(dst != NULL); assert(name != NULL); assert(verify_name(name) == EXIT_SUCCESS); assert(dst->count < MAX_LOCAL_CONTENT_ITEMS); /* First make sure the file is not already in the local content. */ RETURN_IF_FALSE(EXIT_SUCCESS != local_content_find(dst, name, &index)); /* Open the file for binary reading. */ f = fopen(name, "rb"); RETURN_IF_FALSE(f != NULL); /* Determine the length of the file. */ strcpy(dst->items[dst->count].name, name); fseek(f, 0, SEEK_END); size = (size_t)ftell(f); fseek(f, 0, SEEK_SET); /* Check the file is not too long. */ if (size > MAX_CONTENT_LENGTH) { fclose(f); RETURN_FAILURE(); } /* Do not allow zero-length files. */ if (size == 0) { fclose(f); RETURN_FAILURE(); } /* Read the entire file into the buffer. */ if (size != fread(dst->items[dst->count].buffer, 1, size, f)) { fclose(f); RETURN_FAILURE(); } /* Close the file, and return successfully. */ fclose(f); dst->items[dst->count].size = size; dst->count++; return EXIT_SUCCESS; } /* ------------------------------------------------------------------------ */ extern int local_content_find( const local_content_t * src, const char * name, size_t * index) { assert(src != NULL); assert(name != NULL); assert(index != NULL); assert(verify_name(name) == EXIT_SUCCESS); /* Return EXIT_SUCCESS if the item is found. */ for (*index = 0; *index < src->count; (*index)++) if (0 == strcmp(src->items[*index].name, name)) return EXIT_SUCCESS; return EXIT_FAILURE; } /* ------------------------------------------------------------------------ */ extern void local_content_print(const local_content_t * src) { size_t i; assert(src != NULL); printf("Local Content\n"); printf("----------------------------------------\n"); if (src->count == 0) { printf("Empty\n"); return; } for (i = 0; i < src->count; i++) printf("[%u] %s\n", i + 1, src->items[i].name); } /* ------------------------------------------------------------------------ */ extern void request_add(request_cache_t * dst, const char * name) { size_t index; assert(dst != NULL); assert(name != NULL); assert(dst->count <= MAX_REQUESTS); assert(verify_name(name) == EXIT_SUCCESS); /* This will update the time if it is found. */ if (EXIT_SUCCESS == request_find(dst, name, &index)) return; /* Put the item at the end of the array if there is room, or else replace * the oldest item. */ if (dst->count < MAX_REQUESTS) index = dst->count++; else index = find_oldest(dst->time, dst->count); /* Store the item, and set or reset the time. */ strcpy(dst->names[index], name); dst->time[index] = time_now(); } /* ------------------------------------------------------------------------ */ extern int request_expire(request_cache_t * dst1, content_dir_t * dst2) { size_t count; timeval expire; size_t i; assert(dst1 != NULL); assert(dst2 != NULL); assert(dst1->count <= MAX_REQUESTS); assert(dst2->count <= MAX_CONTENT_DIR_ITEMS); /* Any time before this time should be expired. */ expire = time_sub(time_now(), time_seconds(REQUEST_TIMEOUT)); count = 0; /* Loop over each item and expire it if it is too old. */ for (i = 0; i < dst1->count; i++) { if (time_cmp(dst1->time[i], expire) < 0) { count++; printf("\ncontent not found (%s)\n\n", dst1->names[i]); content_dir_remove(dst2, dst1->names[i]); request_remove(dst1, i--); } } return count == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } /* ------------------------------------------------------------------------ */ extern int request_find( request_cache_t * src, const char * name, size_t * index) { assert(src != NULL); assert(name != NULL); assert(index != NULL); assert(src->count <= MAX_REQUESTS); assert(verify_name(name) == EXIT_SUCCESS); /* Reset the time if the item is found. */ for (*index = 0; *index < src->count; (*index)++) { if (strcmp(src->names[*index], name) == 0) { src->time[*index] = time_now(); return EXIT_SUCCESS; } } return EXIT_FAILURE; } /* ------------------------------------------------------------------------ */ extern int request_get_next_timeout(request_cache_t * src, timeval * dst) { size_t index; assert(src != NULL); assert(dst != NULL); if (src->count == 0) return EXIT_FAILURE; index = find_oldest(src->time, src->count); *dst = time_add(src->time[index], time_seconds(REQUEST_TIMEOUT)); return EXIT_SUCCESS; } /* ------------------------------------------------------------------------ */ extern void request_remove(request_cache_t * dst, size_t index) { assert(dst != NULL); assert(dst->count > 0); assert(dst->count <= MAX_REQUESTS); assert(index < dst->count); /* Swap the last item with the replaced one. */ if (index < --dst->count) { strcpy(dst->names[index], dst->names[dst->count]); dst->time[index] = dst->time[dst->count]; } /* Clear the last item to make things look good in the debugger. */ strcpy(dst->names[dst->count], ""); dst->time[dst->count] = time_seconds(0); } /* ------------------------------------------------------------------------ */ extern void request_print(const request_cache_t * src) { size_t i; assert(src != NULL); printf("Request\n"); printf("----------------------------------------\n"); if (src->count == 0) { printf("Empty\n"); return; } for (i = 0; i < src->count; i++) printf("[%u] %-20s %lu.%lu\n", i + 1, src->names[i], src->time[i].tv_sec, src->time[i].tv_usec); } /* ------------------------------------------------------------------------ */ extern int peers_add(peers_t * dst, const peer_t * src) { assert(dst != NULL); assert(src != NULL); assert(dst->count < MAX_PEERS); /* Do not allow duplicate peers from the command line. */ if (EXIT_SUCCESS == find_peer(dst, src)) RETURN_FAILURE(); dst->peers[dst->count++] = *src; return EXIT_SUCCESS; } /* ------------------------------------------------------------------------ */ extern void peers_combine( peers_t * dst, const content_dir_t * src1, const peers_t * src2) { size_t i; assert(dst != NULL); assert(src1 != NULL); assert(src2 != NULL); /* First add the peers from the content directory. */ for (i = 0; i < src1->count; i++) peers_add(dst, &src1->items[i].peer); /* Next add peers from the peer collection, but do not add duplicates. */ for (i = 0; i < src2->count; i++) if (EXIT_SUCCESS != find_peer(dst, &src2->peers[i])) peers_add(dst, &src2->peers[i]); } /* ------------------------------------------------------------------------ */ extern void peers_print(const peers_t * src) { size_t i; assert(src != NULL); printf("Peers\n"); printf("----------------------------------------\n"); if (src->count == 0) { printf("Empty\n"); return; } for (i = 0; i < src->count; i++) { char ip[16]; inet_ntop(AF_INET, &src->peers[i].addr, ip, sizeof(ip)); printf("[%u] %s:%hu\n", i + 1, ip, src->peers[i].port); } } /* ------------------------------------------------------------------------ */ /** * Finds the oldest time in an array of time values. * * @param time The time values. * @param count The number of items in the array. * * @return The index of the oldest item. */ static size_t find_oldest(const timeval * time, size_t count) { size_t i; size_t index; assert(time != NULL); assert(count > 0); /* Assume the first item is the oldest. */ index = 0; /* Search for anything older. */ for (i = 1; i < count; i++) if (time_cmp(time[index], time[i]) > 0) index = i; return index; } /* ------------------------------------------------------------------------ */ /** * Returns EXIT_SUCCESS if a peer exists in a peer collection. * * @param peers The peer collection. * @param peer The peer to find. * * @return EXIT_SUCCESS or EXIT_FAILURE. */ static int find_peer(const peers_t * peers, const peer_t * peer) { size_t i; assert(peers != NULL); assert(peer != NULL); /* Check each peer in the array for a matching address and port. */ for (i = 0; i < peers->count; i++) if (peer_cmp(&peers->peers[i], peer) == 0) return EXIT_SUCCESS; return EXIT_FAILURE; } /* ------------------------------------------------------------------------ */ #ifdef TESTS static int test() { content_dir_t c_cache; data_cache_t d_cache; local_content_t l_content; request_cache_t requests; peers peers; peer_t peer; size_t index; peer.addr = 0x01020304; peer.port = 1234; memset(&c_cache, 0, sizeof(c_cache)); content_dir_add(&c_cache, "jade", &peer); content_dir_add(&c_cache, "apple", &peer); content_dir_add(&c_cache, "banana", &peer); content_dir_add(&c_cache, "cherry", &peer); content_dir_add(&c_cache, "durian", &peer); content_dir_add(&c_cache, "eggplant", &peer); content_dir_print(&c_cache); if (EXIT_SUCCESS == content_dir_find(&c_cache, "eggplant", &index)) printf("the index of element \"eggplant\" is: %d\n", index); memset(&d_cache, 0, sizeof(d_cache)); #define CASE(E) data_cache_add(&d_cache, E, (u_char *)E "'s data", strlen(E) + 8); CASE("jade") CASE("apple") CASE("banana") CASE("cherry") CASE("durian") #undef CASE data_cache_print(&d_cache); if (EXIT_SUCCESS == data_cache_find(&d_cache, "jade", &index)) printf("the index of element \"jade\" is: %d\n", index); memset(&l_content, 0, sizeof(l_content)); local_content_add(&l_content, "file1"); local_content_add(&l_content, "file2"); local_content_add(&l_content, "file3"); local_content_add(&l_content, "file4"); local_content_add(&l_content, "file5"); local_content_add(&l_content, "file6"); local_content_print(&l_content); if (EXIT_SUCCESS == local_content_find(&l_content, "file4", &index)) printf("the index of element \"file4\" is: %d\n", index); memset(&requests, 0, sizeof(requests)); request_add(&requests, "file1"); sleep(2); request_add(&requests, "file2"); sleep(2); request_add(&requests, "file3"); sleep(2); request_add(&requests, "file4"); sleep(2); request_add(&requests, "file5"); sleep(2); request_add(&requests, "file6"); request_print(&requests); sleep(2); request_expire(&requests); request_print(&requests); memset(&peers, 0, sizeof(peers)); peers_add(&peers, &peer); peers_add(&peers, &peer); peers_add(&peers, &peer); peers_print(&peers); return EXIT_SUCCESS; } #endif /*TESTS*/