rounding
[oweals/gnunet.git] / src / datacache / datacache.c
1 /*
2      This file is part of GNUnet
3      (C) 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file datacache/datacache.c
23  * @brief datacache API implementation
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_datacache_lib.h"
29 #include "plugin_datacache.h"
30
31 /**
32  * Internal state of the datacache library.
33  */
34 struct GNUNET_DATACACHE_Handle
35 {
36
37   /**
38    * Bloomfilter to quickly tell if we don't have the content.
39    */
40   struct GNUNET_CONTAINER_BloomFilter *filter;
41
42   /**
43    * Our configuration.
44    */
45   const struct GNUNET_CONFIGURATION_Handle *cfg;
46
47   /**
48    * Configuration section to use.
49    */
50   char *section;
51
52   /**
53    * API of the transport as returned by the plugin's
54    * initialization function.
55    */
56   struct GNUNET_DATACACHE_PluginFunctions *api;
57
58   /**
59    * Short name for the plugin (i.e. "sqlite").
60    */
61   char *short_name;
62
63   /**
64    * Name of the library (i.e. "gnunet_plugin_datacache_sqlite").
65    */
66   char *lib_name;
67   
68   /**
69    * Name for the bloom filter file.
70    */
71   char *bloom_name;
72
73   /**
74    * Environment provided to our plugin.
75    */
76   struct GNUNET_DATACACHE_PluginEnvironment env;
77
78   /**
79    * How much space is in use right now?
80    */
81   unsigned long long utilization;
82
83 };
84
85
86 /**
87  * Function called by plugins to notify the datacache
88  * about content deletions.
89  * 
90  * @param cls closure
91  * @param key key of the content that was deleted
92  * @param size number of bytes that were made available
93  */
94 static void 
95 env_delete_notify (void *cls,
96                    const GNUNET_HashCode *key,
97                    uint32_t size)
98 {
99   struct GNUNET_DATACACHE_Handle * h = cls;
100   GNUNET_assert (h->utilization >= size);
101   h->utilization -= size;
102   GNUNET_CONTAINER_bloomfilter_remove (h->filter, key);
103 }
104
105
106 /**
107  * Create a data cache.
108  *
109  * @param sched scheduler to use
110  * @param cfg configuration to use
111  * @param section section in the configuration that contains our options
112  * @return handle to use to access the service
113  */
114 struct GNUNET_DATACACHE_Handle *
115 GNUNET_DATACACHE_create (struct GNUNET_SCHEDULER_Handle *sched,
116                          const struct GNUNET_CONFIGURATION_Handle *cfg,
117                          const char *section)
118 {
119   unsigned int bf_size;
120   unsigned long long quota;
121   struct GNUNET_DATACACHE_Handle *ret;
122   char *libname;
123   char *name;
124
125   if (GNUNET_OK !=
126       GNUNET_CONFIGURATION_get_value_number (cfg,
127                                              section, "QUOTA", &quota))
128     {
129       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
130                   _("No `%s' specified for `%s' in configuration!\n"),
131                   "QUOTA",
132                   section);
133       return NULL;
134     }
135   if (GNUNET_OK !=
136       GNUNET_CONFIGURATION_get_value_string (cfg,
137                                              section,
138                                              "DATABASE", &name))
139     {
140       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
141                   _("No `%s' specified for `%s' in configuration!\n"),
142                   "DATABASE",
143                   section);
144       return NULL;
145     }
146   bf_size = quota / 32; /* 8 bit per entry, 1 bit per 32 kb in DB */
147
148   ret = GNUNET_malloc (sizeof(struct GNUNET_DATACACHE_Handle));
149   ret->bloom_name = GNUNET_DISK_mktemp ("gnunet-datacachebloom");
150   if (NULL != ret->bloom_name)
151     {
152       ret->filter = GNUNET_CONTAINER_bloomfilter_load (ret->bloom_name, 
153                                                        quota / 1024,    /* 8 bit per entry in DB, expect 1k entries */
154                                                        5);
155     }
156   else
157     {
158       ret->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, bf_size, 5);  /* approx. 3% false positives at max use */  
159     }
160   ret->section = GNUNET_strdup (section);
161   ret->env.sched = sched;
162   ret->env.cfg = cfg;
163   ret->env.delete_notify = &env_delete_notify;  
164   ret->env.section = ret->section;
165   ret->env.cls = ret;
166   ret->env.delete_notify = &env_delete_notify;
167   ret->env.quota = quota;
168   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
169               _("Loading `%s' datacache plugin\n"), name);
170   GNUNET_asprintf (&libname, "libgnunet_plugin_datacache_%s", name);
171   ret->short_name = name;
172   ret->lib_name = libname;
173   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
174   if (ret->api == NULL)
175     {
176       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
177                   _("Failed to load datacache plugin for `%s'\n"), name);
178       GNUNET_DATACACHE_destroy (ret);
179       return NULL;
180     }
181   return ret;
182 }
183
184
185 /**
186  * Destroy a data cache (and free associated resources).
187  *
188  * @param h handle to the datastore
189  */
190 void GNUNET_DATACACHE_destroy (struct GNUNET_DATACACHE_Handle *h)
191 {
192   if (h->filter != NULL)
193     GNUNET_CONTAINER_bloomfilter_free (h->filter);
194   if (h->api != NULL)
195     GNUNET_break (NULL == GNUNET_PLUGIN_unload (h->lib_name, h->api));
196   GNUNET_free (h->lib_name);
197   GNUNET_free (h->short_name);
198   GNUNET_free (h->section);
199   if (h->bloom_name != NULL)
200     {
201       if (0 != UNLINK (h->bloom_name))
202         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
203                                   "unlink",
204                                   h->bloom_name);
205       GNUNET_free (h->bloom_name);
206     }
207   GNUNET_free (h);
208 }
209
210
211 /**
212  * Store an item in the datastore.
213  *
214  * @param h handle to the datacache
215  * @param key key to store data under
216  * @param size number of bytes in data
217  * @param data data to store
218  * @param type type of the value
219  * @param discard_time when to discard the value in any case
220  * @return GNUNET_OK on success, GNUNET_SYSERR on error (full, etc.)
221  */
222 int 
223 GNUNET_DATACACHE_put (struct GNUNET_DATACACHE_Handle *h,
224                       const GNUNET_HashCode * key,
225                       uint32_t size,
226                       const char *data,
227                       unsigned int type,
228                       struct GNUNET_TIME_Absolute discard_time)
229 {
230   uint32_t used;
231
232   used = h->api->put (h->api->cls,
233                       key,
234                       size,
235                       data,
236                       type,
237                       discard_time);
238   if (used == 0)
239     return GNUNET_SYSERR;
240   GNUNET_CONTAINER_bloomfilter_add (h->filter, key);
241   while (h->utilization + used > h->env.quota)
242     GNUNET_assert (GNUNET_OK == h->api->del (h->api->cls));
243   h->utilization += used;
244   return GNUNET_OK;
245 }
246
247
248 /**
249  * Iterate over the results for a particular key
250  * in the datacache.
251  *
252  * @param h handle to the datacache
253  * @param key what to look up
254  * @param type entries of which type are relevant?
255  * @param iter maybe NULL (to just count)
256  * @param iter_cls closure for iter
257  * @return the number of results found
258  */
259 unsigned int 
260 GNUNET_DATACACHE_get (struct GNUNET_DATACACHE_Handle *h,
261                       const GNUNET_HashCode * key,
262                       unsigned int type, 
263                       GNUNET_DATACACHE_Iterator iter,
264                       void *iter_cls)
265 {
266   if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_test (h->filter,
267                                                       key))
268     return 0; /* can not be present */
269   return h->api->get (h->api->cls,
270                       key,
271                       type,
272                       iter,
273                       iter_cls);
274 }
275
276
277
278 /* end of datacache_api.c */