/*
* 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*/