/common/stringbucket.c
/* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
* BSD-style license. Please see the COPYING file for details.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stringbucket.h>
#define STRINGBUCKET_STRINGSIZE 64
const char * strnchr(const char *s, int c, int n) {
const char *ptr = s;
while (n > 0) {
if (*ptr == c)
return ptr;
ptr++;
n--;
}
return NULL;
}
int stringbucket_remap(struct stringbucket *sb) {
struct stat st;
if (sb->list != NULL) {
munmap(sb->list, sb->size);
}
flock(sb->fd, LOCK_SH);
fstat(sb->fd, &st);
flock(sb->fd, LOCK_UN);
sb->size = st.st_size;
if (sb->size > 0) {
sb->list = mmap(NULL, sb->size, PROT_READ | PROT_WRITE, MAP_SHARED, sb->fd, 0);
if (sb->list == MAP_FAILED) {
perror("stringbucket mmap");
return 0;
}
} else {
/* Don't map anything for now. */
sb->list = NULL;
}
return 1;
}
struct stringbucket * stringbucket_open(const char *filename) {
struct stringbucket *obj = malloc(sizeof(struct stringbucket));
obj->list = NULL;
obj->size = 0;
if (obj == NULL) {
perror("stringbucket allocate");
goto stringbucket_open__malloc_fail;
}
obj->fd = open(filename, O_RDWR | O_APPEND | O_CREAT, 0600);
if (obj->fd == -1) {
perror("stringbucket open");
goto stringbucket_open__open_fail;
}
if (stringbucket_remap(obj) == 0) {
goto stringbucket_open__mmap_fail;
}
return obj;
stringbucket_open__mmap_fail:
close(obj->fd);
stringbucket_open__open_fail:
free(obj);
stringbucket_open__malloc_fail:
return NULL;
}
void stringbucket_close(struct stringbucket *sb) {
if (sb == NULL)
return;
if (sb->list != NULL)
munmap(sb->list, sb->size);
close(sb->fd);
free(sb);
}
int stringbucket_find(struct stringbucket *sb, const char *string) {
if (sb->list == NULL)
return -1;
char * end = sb->list + sb->size;
int string_len = strlen(string);
char * ptr = sb->list;
while (ptr < end) {
char * next = (char *) strnchr(ptr, '\n', end - ptr);
if (next == NULL)
next = end;
int len = next - ptr;
if (len > STRINGBUCKET_STRINGSIZE)
len = STRINGBUCKET_STRINGSIZE;
if (memcmp(ptr, string, (len < string_len ? string_len : len)) == 0) {
return (ptr - sb->list);
}
ptr = next + 1;
}
return -1;
}
int stringbucket_add(struct stringbucket *sb, const char *string) {
if (stringbucket_find(sb, string) != -1) return 0;
int str_len = strlen(string);
int len;
flock(sb->fd, LOCK_EX);
len = write(sb->fd, string, str_len);
if (len < 0)
perror("stringbucket add");
if (len < str_len)
goto stringbucket_add__fail;
len = write(sb->fd, "\n", 1);
if (len < 0)
perror("stringbucket add");
if (len == 0)
goto stringbucket_add__fail;
flock(sb->fd, LOCK_UN);
/* remap the data to include added content */
if (stringbucket_remap(sb) == 0)
return 0;
return 1;
stringbucket_add__fail:
ftruncate(sb->fd, sb->size);
flock(sb->fd, LOCK_UN);
return 0;
}
int stringbucket_delete(struct stringbucket *sb, const char *string) {
int pos = stringbucket_find(sb, string);
if (pos == -1) return 0;
/* We doin' it DOS style! */
sb->list[pos] = 0;
return 1;
}
void stringbucket_iterate(struct stringbucket *sb, void (*iter)(char *, void *), void *stuff) {
if (sb->list == NULL)
return;
char string[STRINGBUCKET_STRINGSIZE + 1];
char * ptr = sb->list;
char * end = sb->list + sb->size;
while (ptr < end) {
char * next = (char *) strnchr(ptr, '\n', end - ptr);
if (next == NULL)
next = end;
if (ptr[0] == 0) {
ptr = next + 1;
continue;
}
int len = next - ptr;
memcpy(string, ptr, len);
string[len] = 0;
iter(string, stuff);
ptr = next + 1;
}
}