Add pluggable support for multiple storage backends.
[bluesky.git] / bluesky / store.c
1 /* Blue Sky: File Systems in the Cloud
2  *
3  * Copyright (C) 2009  The Regents of the University of California
4  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
5  *
6  * TODO: Licensing
7  */
8
9 #include <stdint.h>
10 #include <glib.h>
11 #include <string.h>
12
13 #include "bluesky.h"
14
15 /* Interaction with cloud storage.  We expose very simple GET/PUT style
16  * interface, which different backends can implement.  Available backends
17  * (will) include Amazon S3 and a simple local store for testing purposes. */
18
19 struct _BlueSkyStore {
20     const BlueSkyStoreImplementation *impl;
21     gpointer handle;
22 };
23
24 GHashTable *store_implementations;
25
26 void bluesky_store_register(const BlueSkyStoreImplementation *impl,
27                             const gchar *name)
28 {
29     g_hash_table_insert(store_implementations, g_strdup(name), (gpointer)impl);
30 }
31
32 BlueSkyStore *bluesky_store_new(const gchar *type)
33 {
34     const BlueSkyStoreImplementation *impl;
35
36     impl = g_hash_table_lookup(store_implementations, type);
37     if (impl == NULL)
38         return NULL;
39
40     gpointer handle = impl->create();
41     if (handle == NULL)
42         return NULL;
43
44     BlueSkyStore *store = g_new(BlueSkyStore, 1);
45     store->impl = impl;
46     store->handle = handle;
47     return store;
48 }
49
50 void bluesky_store_free(BlueSkyStore *store)
51 {
52     store->impl->destroy(store->handle);
53     g_free(store);
54 }
55
56 BlueSkyRCStr *bluesky_store_get(BlueSkyStore *store, const gchar *key)
57 {
58     return store->impl->get(store->handle, key);
59 }
60
61 void bluesky_store_put(BlueSkyStore *store,
62                        const gchar *key, BlueSkyRCStr *val)
63 {
64     store->impl->put(store->handle, key, val);
65 }
66
67 /* Create and return a new reference-counted string.  The reference count is
68  * initially one.  The newly-returned string takes ownership of the memory
69  * pointed at by data, and will call g_free on it when the reference count
70  * drops to zero. */
71 BlueSkyRCStr *bluesky_string_new(gpointer data, gsize len)
72 {
73     BlueSkyRCStr *string = g_new(BlueSkyRCStr, 1);
74     string->data = data;
75     string->len = len;
76     g_atomic_int_set(&string->refcount, 1);
77     return string;
78 }
79
80 void bluesky_string_ref(BlueSkyRCStr *string)
81 {
82     if (string == NULL)
83         return;
84
85     g_atomic_int_inc(&string->refcount);
86 }
87
88 void bluesky_string_unref(BlueSkyRCStr *string)
89 {
90     if (string == NULL)
91         return;
92
93     if (g_atomic_int_dec_and_test(&string->refcount)) {
94         g_free(string->data);
95         g_free(string);
96     }
97 }
98
99 /* Duplicate and return a new reference-counted string, containing a copy of
100  * the original data, with a reference count of 1.  As an optimization, if the
101  * passed-in string already has a reference count of 1, the original is
102  * returned.   Can be used to make a mutable copy of a shared string.  For this
103  * to truly be safe, it is probably needed that there be some type of lock
104  * protecting access to the string. */
105 BlueSkyRCStr *bluesky_string_dup(BlueSkyRCStr *string)
106 {
107     if (string == NULL)
108         return NULL;
109
110     if (g_atomic_int_dec_and_test(&string->refcount)) {
111         /* There are no other shared copies, so return this one. */
112         g_atomic_int_inc(&string->refcount);
113         return string;
114     } else {
115         return bluesky_string_new(g_memdup(string->data, string->len),
116                                   string->len);
117     }
118 }
119
120 /* Simple in-memory data store for test purposes. */
121 typedef struct {
122     GMutex *lock;
123
124     /* TODO: A hashtable isn't optimal for list queries... */
125     GHashTable *store;
126 } MemStore;
127
128 static gpointer memstore_create()
129 {
130     MemStore *store = g_new(MemStore, 1);
131     store->lock = g_mutex_new();
132     store->store = g_hash_table_new_full(g_str_hash, g_str_equal,
133                                          g_free,
134                                          (GDestroyNotify)bluesky_string_unref);
135
136     return (gpointer)store;
137 }
138
139 static void memstore_destroy(gpointer store)
140 {
141     /* TODO */
142 }
143
144 static BlueSkyRCStr *memstore_get(gpointer st, const gchar *key)
145 {
146     MemStore *store = (MemStore *)st;
147     BlueSkyRCStr *s = g_hash_table_lookup(store->store, key);
148     if (s != NULL)
149         bluesky_string_ref(s);
150     return s;
151 }
152
153 static void memstore_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
154 {
155     MemStore *store = (MemStore *)s;
156     bluesky_string_ref(val);
157     g_hash_table_insert(store->store, g_strdup(key), val);
158 }
159
160 static BlueSkyStoreImplementation memstore_impl = {
161     .create = memstore_create,
162     .destroy = memstore_destroy,
163     .get = memstore_get,
164     .put = memstore_put,
165 };
166
167 /* Store implementation which writes data as files to disk. */
168 static gpointer filestore_create()
169 {
170     return GINT_TO_POINTER(1);
171 }
172
173 static void filestore_destroy()
174 {
175 }
176
177 static BlueSkyRCStr *filestore_get(gpointer s, const gchar *key)
178 {
179     gchar *contents = NULL;
180     gsize length;
181     GError *error;
182
183     g_file_get_contents(key, &contents, &length, &error);
184     if (contents == NULL)
185         return NULL;
186
187     return bluesky_string_new(contents, length);
188 }
189
190 static void filestore_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
191 {
192     g_file_set_contents(key, val->data, val->len, NULL);
193 }
194
195 static BlueSkyStoreImplementation filestore_impl = {
196     .create = filestore_create,
197     .destroy = filestore_destroy,
198     .get = filestore_get,
199     .put = filestore_put,
200 };
201
202 void bluesky_store_init()
203 {
204     store_implementations = g_hash_table_new(g_str_hash, g_str_equal);
205     bluesky_store_register(&memstore_impl, "mem");
206     bluesky_store_register(&filestore_impl, "file");
207 }