data.c

/*
 * 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*/
Valid HTML 4.01 Valid CSS