Add stringring implementation
[blerg.git] / common / stringring.c
1 /* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
2  * BSD-style license.  Please see the COPYING file for details.
3  */
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <sys/mman.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <time.h>
14 #include <stringring.h>
15
16 #define STRINGRING_DATA_CHECK() \
17         if (sh == NULL)         \
18                 return 0;       \
19         if (data == NULL)       \
20                 return 0;       \
21         if (data[0] == 0)       \
22                 return 0;
23
24
25 struct stringring_handle * stringring_open(const char *filename) {
26         struct stringring_handle *sh;
27         struct stat st;
28         int initialize = 0;
29
30         sh = malloc(sizeof(struct stringring_handle));
31         if (sh == NULL) {
32                 perror("stringring_handle malloc");
33                 return NULL;
34         }
35
36         sh->fd = open(filename, O_RDWR | O_CREAT, 0600);
37         flock(sh->fd, LOCK_EX);
38         if (sh->fd == -1) {
39                 perror("stringring open");
40                 goto stringring_open__open_failed;
41         }
42
43         fstat(sh->fd, &st);
44         if (st.st_size < sizeof(struct stringring)) {
45                 ftruncate(sh->fd, sizeof(struct stringring));
46                 initialize = 1;
47         }
48         sh->sr = mmap(NULL, sizeof(struct stringring), PROT_READ | PROT_WRITE, MAP_SHARED, sh->fd, 0);
49         if (sh->sr == MAP_FAILED) {
50                 perror("stringring mmap");
51                 goto stringring_open__map_failed;
52         }
53
54         if (initialize) {
55                 stringring_clear(sh);
56         }
57         flock(sh->fd, LOCK_UN);
58
59         return sh;
60
61 stringring_open__map_failed:
62         flock(sh->fd, LOCK_UN);
63         fclose(sh->fd);
64 stringring_open__open_failed:
65         free(sh);
66         return NULL;
67 }
68
69 void stringring_close(struct stringring_handle *sh) {
70         if (sh == NULL)
71                 return;
72
73         if (sh->sr != NULL)
74                 munmap(sh->sr, sizeof(struct stringring));
75
76         if (sh->fd > -1)
77                 close(sh->fd);
78
79         free(sh);
80 }
81
82 int stringring_add(struct stringring_handle *sh, const char *data) {
83         STRINGRING_DATA_CHECK();
84
85         flock(sh->fd, LOCK_EX);
86         if (sh->sr->counter == 0) {
87                 sh->sr->counter = STRINGRING_N_ENTRIES - 1;
88         } else {
89                 sh->sr->counter--;
90         }
91
92         int data_len = strlen(data);
93         sh->sr->entries[sh->sr->counter].timestamp = time(NULL);
94         strncpy(sh->sr->entries[sh->sr->counter].data, data, STRINGRING_DATA_SIZE);
95         if (data_len < STRINGRING_DATA_SIZE) {
96                 /* zero out the rest of the data */
97                 memset(sh->sr->entries[sh->sr->counter].data + data_len, 0, STRINGRING_DATA_SIZE - data_len);
98         }
99         flock(sh->fd, LOCK_UN);
100
101         return 1;
102 }
103
104 int stringring_find_unlocked(struct stringring_handle *sh, const char *data, uint64_t cutoff) {
105         int n, i;
106
107         i = sh->sr->counter;
108         for (n = 0; n < STRINGRING_N_ENTRIES; n++) {
109                 if (sh->sr->entries[i].timestamp > 0 &&
110                     sh->sr->entries[i].timestamp > cutoff &&
111                     strncmp(sh->sr->entries[i].data, data, STRINGRING_DATA_SIZE) == 0) {
112                         return i;
113                 }
114                 i = (i + 1) % STRINGRING_N_ENTRIES;
115         }
116
117         return -1;
118 }
119
120 int stringring_find(struct stringring_handle *sh, const char *data, unsigned int max_age) {
121         int ret;
122         uint64_t cutoff = (max_age > 0 ? time(NULL) - max_age : 0);
123
124         flock(sh->fd, LOCK_SH);
125         ret = stringring_find_unlocked(sh, data, cutoff);
126         flock(sh->fd, LOCK_UN);
127
128         return ret;
129 }
130
131 int stringring_remove_index_unlocked(struct stringring_handle *sh, int idx) {
132         if (sh == NULL)
133                 return 0;
134         if (idx < 0 || idx >= STRINGRING_N_ENTRIES)
135                 return 0;
136
137         sh->sr->entries[idx].timestamp = 0;
138         memset(sh->sr->entries[idx].data, 0, STRINGRING_DATA_SIZE);
139
140         return 1;
141 }
142
143 int stringring_remove_index(struct stringring_handle *sh, int idx) {
144         int ret;
145
146         flock(sh->fd, LOCK_EX);
147         ret = stringring_remove_index_unlocked(sh, idx);
148         flock(sh->fd, LOCK_UN);
149
150         return ret;
151 }
152
153 int stringring_remove(struct stringring_handle *sh, const char *data) {
154         STRINGRING_DATA_CHECK();
155         int ret;
156
157         flock(sh->fd, LOCK_EX);
158         ret = stringring_remove_index_unlocked(sh,
159                         stringring_find_unlocked(sh, data, 0));
160         flock(sh->fd, LOCK_UN);
161
162         return ret;
163 }
164
165 int stringring_clear(struct stringring_handle *sh) {
166         if (sh == NULL)
167                 return 0;
168
169         flock(sh->fd, LOCK_EX);
170         memset(sh->sr, 0, sizeof(struct stringring));
171         flock(sh->fd, LOCK_UN);
172
173         return 1;
174 }