Add stringring implementation
CGI_LIBDIRS = $(CGI_UTIL_LIBDIR) $(YAJL_LIBDIR)
targets = blerg.a blerg_auth.a blergtool blerglatest blerg.cgi perl-lib www/build/enyo-blerg.js
-blerg_a_objects = database/database.o database/tags.o database/util.o database/configuration.o database/subscription.o common/stringbucket.o
+blerg_a_objects = database/database.o database/tags.o database/util.o database/configuration.o database/subscription.o common/stringbucket.o common/stringring.o
blerg_auth_a_objects = common/auth.o common/md5.o
blergtool_objects = tools/blergtool.o blerg.a
blerglatest_objects = tools/blerglatest.o blerg.a common/json.o
+/* 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 <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stringring.h>
+
+#define STRINGRING_DATA_CHECK() \
+ if (sh == NULL) \
+ return 0; \
+ if (data == NULL) \
+ return 0; \
+ if (data[0] == 0) \
+ return 0;
+
+
+struct stringring_handle * stringring_open(const char *filename) {
+ struct stringring_handle *sh;
+ struct stat st;
+ int initialize = 0;
+
+ sh = malloc(sizeof(struct stringring_handle));
+ if (sh == NULL) {
+ perror("stringring_handle malloc");
+ return NULL;
+ }
+
+ sh->fd = open(filename, O_RDWR | O_CREAT, 0600);
+ flock(sh->fd, LOCK_EX);
+ if (sh->fd == -1) {
+ perror("stringring open");
+ goto stringring_open__open_failed;
+ }
+
+ fstat(sh->fd, &st);
+ if (st.st_size < sizeof(struct stringring)) {
+ ftruncate(sh->fd, sizeof(struct stringring));
+ initialize = 1;
+ }
+ sh->sr = mmap(NULL, sizeof(struct stringring), PROT_READ | PROT_WRITE, MAP_SHARED, sh->fd, 0);
+ if (sh->sr == MAP_FAILED) {
+ perror("stringring mmap");
+ goto stringring_open__map_failed;
+ }
+
+ if (initialize) {
+ stringring_clear(sh);
+ }
+ flock(sh->fd, LOCK_UN);
+
+ return sh;
+
+stringring_open__map_failed:
+ flock(sh->fd, LOCK_UN);
+ fclose(sh->fd);
+stringring_open__open_failed:
+ free(sh);
+ return NULL;
+}
+
+void stringring_close(struct stringring_handle *sh) {
+ if (sh == NULL)
+ return;
+
+ if (sh->sr != NULL)
+ munmap(sh->sr, sizeof(struct stringring));
+
+ if (sh->fd > -1)
+ close(sh->fd);
+
+ free(sh);
+}
+
+int stringring_add(struct stringring_handle *sh, const char *data) {
+ STRINGRING_DATA_CHECK();
+
+ flock(sh->fd, LOCK_EX);
+ if (sh->sr->counter == 0) {
+ sh->sr->counter = STRINGRING_N_ENTRIES - 1;
+ } else {
+ sh->sr->counter--;
+ }
+
+ int data_len = strlen(data);
+ sh->sr->entries[sh->sr->counter].timestamp = time(NULL);
+ strncpy(sh->sr->entries[sh->sr->counter].data, data, STRINGRING_DATA_SIZE);
+ if (data_len < STRINGRING_DATA_SIZE) {
+ /* zero out the rest of the data */
+ memset(sh->sr->entries[sh->sr->counter].data + data_len, 0, STRINGRING_DATA_SIZE - data_len);
+ }
+ flock(sh->fd, LOCK_UN);
+
+ return 1;
+}
+
+int stringring_find_unlocked(struct stringring_handle *sh, const char *data, uint64_t cutoff) {
+ int n, i;
+
+ i = sh->sr->counter;
+ for (n = 0; n < STRINGRING_N_ENTRIES; n++) {
+ if (sh->sr->entries[i].timestamp > 0 &&
+ sh->sr->entries[i].timestamp > cutoff &&
+ strncmp(sh->sr->entries[i].data, data, STRINGRING_DATA_SIZE) == 0) {
+ return i;
+ }
+ i = (i + 1) % STRINGRING_N_ENTRIES;
+ }
+
+ return -1;
+}
+
+int stringring_find(struct stringring_handle *sh, const char *data, unsigned int max_age) {
+ int ret;
+ uint64_t cutoff = (max_age > 0 ? time(NULL) - max_age : 0);
+
+ flock(sh->fd, LOCK_SH);
+ ret = stringring_find_unlocked(sh, data, cutoff);
+ flock(sh->fd, LOCK_UN);
+
+ return ret;
+}
+
+int stringring_remove_index_unlocked(struct stringring_handle *sh, int idx) {
+ if (sh == NULL)
+ return 0;
+ if (idx < 0 || idx >= STRINGRING_N_ENTRIES)
+ return 0;
+
+ sh->sr->entries[idx].timestamp = 0;
+ memset(sh->sr->entries[idx].data, 0, STRINGRING_DATA_SIZE);
+
+ return 1;
+}
+
+int stringring_remove_index(struct stringring_handle *sh, int idx) {
+ int ret;
+
+ flock(sh->fd, LOCK_EX);
+ ret = stringring_remove_index_unlocked(sh, idx);
+ flock(sh->fd, LOCK_UN);
+
+ return ret;
+}
+
+int stringring_remove(struct stringring_handle *sh, const char *data) {
+ STRINGRING_DATA_CHECK();
+ int ret;
+
+ flock(sh->fd, LOCK_EX);
+ ret = stringring_remove_index_unlocked(sh,
+ stringring_find_unlocked(sh, data, 0));
+ flock(sh->fd, LOCK_UN);
+
+ return ret;
+}
+
+int stringring_clear(struct stringring_handle *sh) {
+ if (sh == NULL)
+ return 0;
+
+ flock(sh->fd, LOCK_EX);
+ memset(sh->sr, 0, sizeof(struct stringring));
+ flock(sh->fd, LOCK_UN);
+
+ return 1;
+}
+#ifndef _STRINGRING_H
+#define _STRINGRING_H
+
+#include <stdint.h>
+
+#define STRINGRING_DATA_SIZE 32
+#define STRINGRING_N_ENTRIES 102
+
+struct stringring {
+ uint8_t counter;
+ uint8_t reserved[15];
+ struct {
+ uint64_t timestamp;
+ uint8_t data[STRINGRING_DATA_SIZE];
+ } entries[STRINGRING_N_ENTRIES];
+};
+
+struct stringring_handle {
+ int fd;
+ struct stringring *sr;
+};
+
+struct stringring_handle * stringring_open(const char *filename);
+void stringring_close(struct stringring_handle *sh);
+int stringring_add(struct stringring_handle *sh, const char *data);
+int stringring_find(struct stringring_handle *sh, const char *data, unsigned int max_age);
+int stringring_remove(struct stringring_handle *sh, const char *data);
+int stringring_clear(struct stringring_handle *sh);
+
+#endif /* _STRINGRING_H */