Do not pad the final block of a file with zeroes.
[bluesky.git] / bluesky / store.c
index 9b509e6..52cc6b3 100644 (file)
 #include <glib.h>
 #include <string.h>
 
-#include "bluesky.h"
+#include "bluesky-private.h"
 
 /* Interaction with cloud storage.  We expose very simple GET/PUT style
  * interface, which different backends can implement.  Available backends
  * (will) include Amazon S3 and a simple local store for testing purposes. */
 
-/* Create and return a new reference-counted string.  The reference count is
- * initially one.  The newly-returned string takes ownership of the memory
- * pointed at by data, and will call g_free on it when the reference count
- * drops to zero. */
-BlueSkyRCStr *bluesky_string_new(gpointer data, gsize len)
+struct _BlueSkyStore {
+    const BlueSkyStoreImplementation *impl;
+    gpointer handle;
+};
+
+GHashTable *store_implementations;
+
+void bluesky_store_register(const BlueSkyStoreImplementation *impl,
+                            const gchar *name)
 {
-    BlueSkyRCStr *string = g_new(BlueSkyRCStr, 1);
-    string->data = data;
-    string->len = len;
-    g_atomic_int_set(&string->refcount, 1);
-    return string;
+    g_hash_table_insert(store_implementations, g_strdup(name), (gpointer)impl);
 }
 
-void bluesky_string_ref(BlueSkyRCStr *string)
+BlueSkyStore *bluesky_store_new(const gchar *type)
 {
-    if (string == NULL)
-        return;
+    const BlueSkyStoreImplementation *impl;
 
-    g_atomic_int_inc(&string->refcount);
+    impl = g_hash_table_lookup(store_implementations, type);
+    if (impl == NULL)
+        return NULL;
+
+    gpointer handle = impl->create();
+    if (handle == NULL)
+        return NULL;
+
+    BlueSkyStore *store = g_new(BlueSkyStore, 1);
+    store->impl = impl;
+    store->handle = handle;
+    return store;
 }
 
-void bluesky_string_unref(BlueSkyRCStr *string)
+void bluesky_store_free(BlueSkyStore *store)
 {
-    if (string == NULL)
-        return;
-
-    if (g_atomic_int_dec_and_test(&string->refcount)) {
-        g_free(string->data);
-        g_free(string);
-    }
+    store->impl->destroy(store->handle);
+    g_free(store);
 }
 
-/* Duplicate and return a new reference-counted string, containing a copy of
- * the original data, with a reference count of 1.  As an optimization, if the
- * passed-in string already has a reference count of 1, the original is
- * returned.   Can be used to make a mutable copy of a shared string.  For this
- * to truly be safe, it is probably needed that there be some type of lock
- * protecting access to the string. */
-BlueSkyRCStr *bluesky_string_dup(BlueSkyRCStr *string)
+BlueSkyRCStr *bluesky_store_get(BlueSkyStore *store, const gchar *key)
 {
-    if (string == NULL)
-        return NULL;
+    return store->impl->get(store->handle, key);
+}
 
-    if (g_atomic_int_dec_and_test(&string->refcount)) {
-        /* There are no other shared copies, so return this one. */
-        g_atomic_int_inc(&string->refcount);
-        return string;
-    } else {
-        return bluesky_string_new(g_memdup(string->data, string->len),
-                                  string->len);
-    }
+void bluesky_store_put(BlueSkyStore *store,
+                       const gchar *key, BlueSkyRCStr *val)
+{
+    store->impl->put(store->handle, key, val);
 }
 
 /* Simple in-memory data store for test purposes. */
@@ -77,7 +72,7 @@ typedef struct {
     GHashTable *store;
 } MemStore;
 
-MemStore *memstore_new()
+static gpointer memstore_create()
 {
     MemStore *store = g_new(MemStore, 1);
     store->lock = g_mutex_new();
@@ -85,19 +80,75 @@ MemStore *memstore_new()
                                          g_free,
                                          (GDestroyNotify)bluesky_string_unref);
 
-    return store;
+    return (gpointer)store;
 }
 
-BlueSkyRCStr *memstore_get(MemStore *store, const gchar *key)
+static void memstore_destroy(gpointer store)
 {
+    /* TODO */
+}
+
+static BlueSkyRCStr *memstore_get(gpointer st, const gchar *key)
+{
+    MemStore *store = (MemStore *)st;
     BlueSkyRCStr *s = g_hash_table_lookup(store->store, key);
     if (s != NULL)
         bluesky_string_ref(s);
     return s;
 }
 
-void memstore_put(MemStore *store, const gchar *key, BlueSkyRCStr *val)
+static void memstore_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
 {
+    MemStore *store = (MemStore *)s;
     bluesky_string_ref(val);
     g_hash_table_insert(store->store, g_strdup(key), val);
 }
+
+static BlueSkyStoreImplementation memstore_impl = {
+    .create = memstore_create,
+    .destroy = memstore_destroy,
+    .get = memstore_get,
+    .put = memstore_put,
+};
+
+/* Store implementation which writes data as files to disk. */
+static gpointer filestore_create()
+{
+    return GINT_TO_POINTER(1);
+}
+
+static void filestore_destroy()
+{
+}
+
+static BlueSkyRCStr *filestore_get(gpointer s, const gchar *key)
+{
+    gchar *contents = NULL;
+    gsize length;
+    GError *error = NULL;
+
+    g_file_get_contents(key, &contents, &length, &error);
+    if (contents == NULL)
+        return NULL;
+
+    return bluesky_string_new(contents, length);
+}
+
+static void filestore_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
+{
+    g_file_set_contents(key, val->data, val->len, NULL);
+}
+
+static BlueSkyStoreImplementation filestore_impl = {
+    .create = filestore_create,
+    .destroy = filestore_destroy,
+    .get = filestore_get,
+    .put = filestore_put,
+};
+
+void bluesky_store_init()
+{
+    store_implementations = g_hash_table_new(g_str_hash, g_str_equal);
+    bluesky_store_register(&memstore_impl, "mem");
+    bluesky_store_register(&filestore_impl, "file");
+}