evas/cserve2: Implement shared index and buffers
authorJean-Philippe Andre <jp.andre@samsung.com>
Thu, 18 Jul 2013 10:39:33 +0000 (19:39 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Mon, 28 Oct 2013 06:47:12 +0000 (15:47 +0900)
Only import the C file for now.
Implement the following features:

- Shared Arrays
Store arrays of elements of fixed size in shm.

- Shared Mempool
Store random sized buffers in shm.
These buffers are indexed in a Shared Array and are
referred to using their index only.

- Shared Strings
Store strings in a shm in a way similar to Eina_Stringshare
(except strings are referred to using an int index).

- Include evas_cserve2_index.c to the compilation.
- Declare shared index functions in header file.
- Call init() and shutdown() on the shared index subsystem.
- Add find and foreach functions

src/Makefile_Evas.am
src/bin/evas/evas_cserve2.h
src/bin/evas/evas_cserve2_index.c [new file with mode: 0644]
src/bin/evas/evas_cserve2_main.c

index 29bea4b..de3636b 100644 (file)
@@ -1013,6 +1013,7 @@ bin/evas/evas_cserve2_requests.c \
 bin/evas/evas_cserve2_fonts.c \
 bin/evas/evas_cserve2_scale.c \
 bin/evas/evas_cserve2_main_loop_linux.c \
+bin/evas/evas_cserve2_index.c \
 lib/evas/cserve2/evas_cs2_utils.h \
 lib/evas/cserve2/evas_cs2_utils.c
 
index e7e53f3..b6bdff3 100644 (file)
@@ -316,4 +316,47 @@ void *cserve2_font_slave_cb(Slave_Thread_Data *sd, Slave_Command *cmd, const voi
 void cserve2_font_source_ft_free(void *fontsource);
 void cserve2_font_ft_free(void *fontinfo);
 
+// Shared buffers & indexes
+void cserve2_shared_index_init(void);
+void cserve2_shared_index_shutdown(void);
+
+typedef struct _Shared_Array Shared_Array;
+typedef struct _Shared_Mempool Shared_Mempool;
+typedef Eina_Bool (* Shared_Array_Repack_Skip_Cb) (Shared_Array *, const void *);
+
+// Shared arrays (arrays of fixed size object)
+Shared_Array *cserve2_shared_array_new(int tag, int elemsize, int initcount);
+const char *cserve2_shared_array_name_get(Shared_Array *sa);
+void cserve2_shared_array_del(Shared_Array *sa);
+int cserve2_shared_array_size_get(Shared_Array *sa);
+int cserve2_shared_array_count_get(Shared_Array *sa);
+int cserve2_shared_array_item_size_get(Shared_Array *sa);
+int cserve2_shared_array_generation_id_get(Shared_Array *sa);
+int cserve2_shared_array_size_set(Shared_Array *sa, int newcount);
+int cserve2_shared_array_item_new(Shared_Array *sa);
+void *cserve2_shared_array_item_data_get(Shared_Array *sa, int elemid);
+Shared_Array *cserve2_shared_array_repack(Shared_Array *sa,
+                                          Shared_Array_Repack_Skip_Cb skip,
+                                          Eina_Compare_Cb cmp);
+int cserve2_shared_array_item_find(Shared_Array *sa, void *data,
+                                   Eina_Compare_Cb cmp);
+void *cserve2_shared_array_item_data_find(Shared_Array *sa, void *data,
+                                          Eina_Compare_Cb cmp);
+int cserve2_shared_array_foreach(Shared_Array *sa, Eina_Each_Cb cb, void *data);
+
+// Shared buffers and memory pools
+Shared_Mempool *cserve2_shared_mempool_new(int initsize);
+void cserve2_shared_mempool_del(Shared_Mempool *sm);
+int cserve2_shared_mempool_buffer_new(Shared_Mempool *sm, int size);
+int cserve2_shared_mempool_buffer_ref(Shared_Mempool *sm, int bufferid);
+void cserve2_shared_mempool_buffer_del(Shared_Mempool *sm, int bufferid);
+void *cserve2_shared_mempool_buffer_get(Shared_Mempool *sm, int bufferid);
+
+
+// Shared strings
+int cserve2_shared_string_add(const char *str);
+int cserve2_shared_string_ref(int id);
+void cserve2_shared_string_del(int id);
+const char *cserve2_shared_string_get(int id);
+
 #endif /* _EVAS_CSERVE2_H */
diff --git a/src/bin/evas/evas_cserve2_index.c b/src/bin/evas/evas_cserve2_index.c
new file mode 100644 (file)
index 0000000..2579104
--- /dev/null
@@ -0,0 +1,878 @@
+/* Shared index for cserve2.
+ * EXPERIMENTAL WORK.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "evas_cserve2.h"
+#include "evas_cs2_utils.h"
+
+#include <stdint.h>
+
+typedef struct _Data_Shm Data_Shm;
+typedef struct _Index_Entry Index_Entry;
+typedef struct _Block Block;
+typedef struct _Shared_Array_Header Shared_Array_Header;
+typedef struct _Shared_Index Shared_Index;
+
+static int _instances = 0;
+
+// Static memory pool used for storing strings
+static Shared_Mempool *_string_mempool = NULL;
+
+// Map const char* --> buffer id (valid in _string_mempool)
+static Eina_Hash *_string_entries = NULL;
+
+struct _Data_Shm
+{
+   Shm_Handle *shm;
+   char *data;
+};
+
+struct _Shared_Array_Header
+{
+   int32_t tag;
+   int32_t elemsize;
+   int32_t count;
+   int32_t generation_id;
+   int32_t emptyidx;
+   int32_t _reserved1;
+   int32_t _reserved2;
+   int32_t _reserved3;
+};
+
+struct _Shared_Array
+{
+   Data_Shm *ds;
+   Shared_Array_Header *header;
+};
+
+struct _Shared_Index
+{
+   // Random buffer index
+   Shared_Array *sa;
+   int32_t lastid;
+};
+
+struct _Shared_Mempool
+{
+   Data_Shm *ds;
+   Shared_Index *index;
+   int empty_size;
+   Block *empty_blocks;
+};
+
+// Used for empty blocks. RB tree ordered by length.
+struct _Block
+{
+   EINA_RBTREE;
+   int32_t length;
+   int32_t offset;
+   int32_t shmid;
+};
+
+struct _Index_Entry
+{
+   int32_t id; // Write last, can't be 0
+   int32_t refcount;
+   // Block entry
+   int32_t length;
+   int32_t offset;
+   int32_t shmid;
+};
+
+
+// Data blocks will be aligned to blocks of DATA_BLOCKSIZE bytes to reduce
+// fragmentation (after del). 16 is convenient for debugging with hd :)
+#define DATA_BLOCKSIZE 8
+
+static inline int
+_data_blocksize_roundup(int len)
+{
+   return ((len + DATA_BLOCKSIZE - 1) / DATA_BLOCKSIZE) * DATA_BLOCKSIZE;
+}
+
+static Eina_Rbtree_Direction
+_block_rbtree_cmp(const Eina_Rbtree *l, const Eina_Rbtree *r,
+                  void *data EINA_UNUSED)
+{
+   const Block *left  = (Block *) l;
+   const Block *right = (Block *) r;
+
+   if (!left)
+     return EINA_RBTREE_RIGHT;
+
+   if (!right)
+     return EINA_RBTREE_LEFT;
+
+   if (left->length <= right->length)
+     return EINA_RBTREE_LEFT;
+   else
+     return EINA_RBTREE_RIGHT;
+}
+
+static inline int
+_block_rbtree_empty_spot_find(const Block *node, const void *key,
+                              int key_length EINA_UNUSED,
+                              void *data EINA_UNUSED)
+{
+   int32_t length = (int32_t) (intptr_t) key;
+
+   // Found best spot
+   if (node->length == length)
+     return 0;
+
+   // We're good, can we find better?
+   if (node->length > length)
+     {
+        Block *lson = (Block *) node->__rbtree.son[0];
+
+        if (lson && lson->length >= length)
+          return -1;
+
+        // This is the best we can do...
+        return 0;
+     }
+
+   // Keep calm and carry on
+   return 1;
+}
+
+static inline int
+_block_rbtree_block_find(const Block *node, const void *key,
+                         int key_length EINA_UNUSED,
+                         void *data EINA_UNUSED)
+{
+   const Index_Entry *ie = key;
+
+   // Found best spot
+   if ((node->length == ie->length)
+       && (node->offset == ie->offset)
+       && (node->shmid == ie->shmid))
+     return 0;
+
+   // We're good, can we find better?
+   if (node->length > ie->length)
+     {
+        Block *lson = (Block *) node->__rbtree.son[0];
+
+        if (lson && lson->length >= ie->length)
+          return -1;
+
+        // This is the best we can do...
+        return 0;
+     }
+
+   // Keep calm and carry on
+   return 1;
+}
+
+
+// Data shm
+
+Data_Shm *
+_shared_data_shm_new(int size)
+{
+   Data_Shm *ds;
+   size_t mapping_size;
+
+   if (size <= 0)
+     return NULL;
+
+   ds = calloc(1, sizeof(Data_Shm));
+   if (!ds) return NULL;
+
+   mapping_size = cserve2_shm_size_normalize((size_t) size);
+
+   ds->shm = cserve2_shm_request("data", mapping_size);
+   if (!ds->shm)
+     {
+        ERR("Could not create shm of size %u", (unsigned) mapping_size);
+        free(ds);
+        return NULL;
+     }
+
+   ds->data = cserve2_shm_map(ds->shm);
+   if (!ds->data)
+     {
+        ERR("Could not map shm of size %u", (unsigned) mapping_size);
+        cserve2_shm_unref(ds->shm);
+        free(ds);
+        return NULL;
+     }
+
+   DBG("Created data shm of size %d: %d", size, cserve2_shm_id_get(ds->shm));
+   return ds;
+}
+
+void
+_shared_data_shm_del(Data_Shm *ds)
+{
+   if (!ds) return;
+   cserve2_shm_unref(ds->shm);
+   free(ds);
+}
+
+int
+_shared_data_shm_resize(Data_Shm *ds, size_t newsize)
+{
+   Shm_Handle *shm;
+   size_t mapping_size;
+
+   if (newsize <= 0)
+     return -1;
+
+   mapping_size = cserve2_shm_size_normalize(newsize);
+   cserve2_shm_unmap(ds->shm);
+   ds->data = NULL;
+
+   shm = cserve2_shm_resize(ds->shm, mapping_size);
+   if (!shm)
+     {
+        ERR("Could not resize shm %d to %u",
+            cserve2_shm_id_get(ds->shm), (unsigned) newsize);
+        return -1;
+     }
+
+   ds->shm = shm;
+   ds->data = cserve2_shm_map(ds->shm);
+   if (!ds->data)
+     {
+        ERR("Failed to remap shm %d after resizing to %u bytes",
+            cserve2_shm_id_get(ds->shm), (unsigned) mapping_size);
+        return -1;
+     }
+
+   return mapping_size;
+}
+
+
+// Arrays
+
+Shared_Array *
+cserve2_shared_array_new(int tag, int elemsize, int initcount)
+{
+   Shared_Array *sa;
+   Data_Shm *ds;
+   int mapping_size;
+
+   if (elemsize <= 0 || initcount < 0)
+     return NULL;
+
+   sa = calloc(1, sizeof(Shared_Array));
+   if (!sa) return NULL;
+
+   if (!initcount) initcount = 1;
+   mapping_size = cserve2_shm_size_normalize(elemsize * initcount
+                                             + sizeof(Shared_Array_Header));
+   ds = _shared_data_shm_new(mapping_size);
+   if (!ds)
+     {
+        free(sa);
+        return NULL;
+     }
+
+   sa->ds = ds;
+   sa->header = (Shared_Array_Header *) ds->data;
+   sa->header->count = (mapping_size - sizeof(Shared_Array_Header)) / elemsize;
+   sa->header->elemsize = elemsize;
+   sa->header->generation_id = 1;
+   sa->header->emptyidx = 0;
+   sa->header->tag = tag;
+   memset(&sa->header->_reserved1, 0, sizeof(int32_t) * 3);
+
+   return sa;
+}
+
+const char *
+cserve2_shared_array_name_get(Shared_Array *sa)
+{
+   if (!sa) return NULL;
+   return cserve2_shm_name_get(sa->ds->shm);
+}
+
+void
+cserve2_shared_array_del(Shared_Array *sa)
+{
+   if (!sa) return;
+   _shared_data_shm_del(sa->ds);
+   free(sa);
+}
+
+int
+cserve2_shared_array_size_get(Shared_Array *sa)
+{
+   if (!sa) return -1;
+   return sa->header->count;
+}
+
+int
+cserve2_shared_array_count_get(Shared_Array *sa)
+{
+   if (!sa) return -1;
+   return sa->header->emptyidx;
+}
+
+int
+cserve2_shared_array_item_size_get(Shared_Array *sa)
+{
+   if (!sa) return -1;
+   return sa->header->elemsize;
+}
+
+int
+cserve2_shared_array_generation_id_get(Shared_Array *sa)
+{
+   if (!sa) return -1;
+   return sa->header->generation_id;
+}
+
+int
+cserve2_shared_array_size_set(Shared_Array *sa, int newcount)
+{
+   int mapping_size;
+
+   if (!sa) return -1;
+   mapping_size = cserve2_shm_size_normalize(sa->header->elemsize * newcount
+                                             + sizeof(Shared_Array_Header));
+   if (_shared_data_shm_resize(sa->ds, mapping_size) < 0)
+     {
+        sa->header = NULL;
+        return -1;
+     }
+   sa->header = (Shared_Array_Header *) sa->ds->data;
+   sa->header->count = (mapping_size - sizeof(Shared_Array_Header))
+         / sa->header->elemsize;
+
+   return sa->header->count;
+}
+
+int
+cserve2_shared_array_item_new(Shared_Array *sa)
+{
+   if (!sa) return -1;
+   if (sa->header->emptyidx >= sa->header->count)
+     {
+        int newcount = cserve2_shared_array_size_set(sa, sa->header->count + 1);
+        if (newcount < 0) return -1;
+     }
+
+   return sa->header->emptyidx++;
+}
+
+const void *
+cserve2_shared_array_item_data(Shared_Array *sa)
+{
+   return cserve2_shared_array_item_data_get(sa, 0);
+}
+
+void *
+cserve2_shared_array_item_data_get(Shared_Array *sa, int elemid)
+{
+   char *ptr;
+
+   if (!sa) return NULL;
+   if (elemid < 0 || elemid >= sa->header->count)
+     return NULL;
+
+   ptr  = (char *) sa->header;
+   ptr += sizeof(Shared_Array_Header);
+   ptr += elemid * sa->header->elemsize;
+
+   return ptr;
+}
+
+int
+cserve2_shared_array_foreach(Shared_Array *sa, Eina_Each_Cb cb, void *data)
+{
+   char *ptr;
+   int k;
+
+   if (!sa || !cb) return -1;
+   ptr = sa->ds->data + sizeof(Shared_Array_Header);
+
+   for (k = 0; k < sa->header->emptyidx; k++)
+     {
+        if (!cb(sa, ptr, data))
+          break;
+        ptr += sa->header->elemsize;
+     }
+
+   return k;
+}
+
+Shared_Array *
+cserve2_shared_array_repack(Shared_Array *sa,
+                            Shared_Array_Repack_Skip_Cb skip,
+                            Eina_Compare_Cb cmp)
+{
+   Eina_List *l = NULL;
+   Shared_Array *sa2;
+   const char *srcdata;
+   char *dstdata;
+   int k, elemsize, newcount = 0;
+
+   if (!sa || !skip || !cmp) return NULL;
+   srcdata = sa->ds->data + sizeof(Shared_Array_Header);
+   elemsize = sa->header->elemsize;
+
+   // Build ordered list of pointers
+   for (k = 0; k < sa->header->emptyidx; k++)
+     {
+        const char *data = srcdata + k * elemsize;
+        if (skip(sa, data)) continue;
+        l = eina_list_sorted_insert(l, cmp, data);
+        newcount++;
+     }
+
+   // Create new array
+   sa2 = cserve2_shared_array_new(0, elemsize, newcount);
+   if (!sa)
+     {
+        ERR("Can not repack array: failed to create new array");
+        return NULL;
+     }
+   sa2->header->generation_id = sa->header->generation_id + 1;
+
+   // Write data
+   dstdata = sa2->ds->data + sizeof(Shared_Array_Header);
+   while (l)
+     {
+        const char *data = eina_list_data_get(l);
+        l = eina_list_remove_list(l, l);
+        memcpy(dstdata, data, elemsize);
+     }
+
+   // Finalize & return
+   sa2->header->emptyidx = newcount;
+   sa2->header->tag = sa->header->tag;
+   return sa2;
+}
+
+int
+cserve2_shared_array_item_find(Shared_Array *sa, void *data,
+                               Eina_Compare_Cb cmp)
+{
+   int k;
+   const char *ptr;
+
+   if (!sa || !cmp) return -1;
+
+   // TODO: Fast search in the sorted zone
+
+   ptr = sa->ds->data;
+
+   // Linear search O(n)
+   for (k = 0; k < sa->header->emptyidx; k++)
+     {
+        if (!cmp(ptr, data))
+          return k;
+        ptr += sa->header->elemsize;
+     }
+
+   return -1;
+}
+
+void *
+cserve2_shared_array_item_data_find(Shared_Array *sa, void *data,
+                                    Eina_Compare_Cb cmp)
+{
+   int elemid;
+
+   elemid = cserve2_shared_array_item_find(sa, data, cmp);
+   if (elemid < 0) return NULL;
+
+   return cserve2_shared_array_item_data_get(sa, elemid);
+}
+
+
+// Shared index (used by the random mempool)
+
+static Index_Entry *
+_shared_index_entry_new(Shared_Index *si)
+{
+   Index_Entry *ie;
+   int id;
+
+   if (!si) return NULL;
+
+   id = cserve2_shared_array_item_new(si->sa);
+   if (id < 0) return NULL;
+
+   ie = cserve2_shared_array_item_data_get(si->sa, id);
+   ie->id = si->lastid++;
+   return ie;
+}
+
+static Index_Entry *
+_shared_index_entry_find(Shared_Index *si, int id)
+{
+   int k, count;
+
+   if (!si) return NULL;
+   count = cserve2_shared_array_count_get(si->sa);
+
+   // FIXME: Linear search O(n)
+   for (k = 0; k < count; k++)
+     {
+        Index_Entry *ie;
+        ie = cserve2_shared_array_item_data_get(si->sa, k);
+        if (!ie) break;
+        if (ie->id == id)
+          return ie;
+     }
+
+   return NULL;
+}
+
+static Shared_Index *
+_shared_index_new()
+{
+   Shared_Index *si;
+   Index_Entry *ie;
+   int tag = 1234; // FIXME?
+
+   si = calloc(1, sizeof(Shared_Index));
+   if (!si) return NULL;
+
+   si->sa = cserve2_shared_array_new(tag, sizeof(Index_Entry), 1);
+   if (!si->sa)
+     {
+        free(si);
+        return NULL;
+     }
+
+   si->lastid = 0;
+   ie = _shared_index_entry_new(si);
+   if (!ie)
+     {
+        cserve2_shared_array_del(si->sa);
+        free(si);
+        return NULL;
+     }
+
+   return si;
+}
+
+void
+_shared_index_del(Shared_Index *si)
+{
+   if (!si) return;
+   cserve2_shared_array_del(si->sa);
+   free(si);
+}
+
+
+// Shared memory pool
+
+Shared_Mempool *
+cserve2_shared_mempool_new(int initsize)
+{
+   Shared_Mempool *sm;
+   size_t mapping_size;
+
+   if (initsize < 0) return NULL;
+
+   sm = calloc(1, sizeof(Shared_Mempool));
+   if (!sm) return NULL;
+
+   if (!initsize) initsize = 1;
+   mapping_size = cserve2_shm_size_normalize((size_t) initsize);
+
+   sm->ds = _shared_data_shm_new(mapping_size);
+   if (!sm->ds)
+     {
+        free(sm);
+        return NULL;
+     }
+
+   sm->index = _shared_index_new();
+   if (!sm->index)
+     {
+        _shared_data_shm_del(sm->ds);
+        free(sm);
+        return NULL;
+     }
+
+   sm->empty_size = mapping_size;
+   return sm;
+}
+
+static void
+_shared_mempool_block_del(Eina_Rbtree *node, void *data EINA_UNUSED)
+{
+   Block *blk = (Block *) node;
+   free(blk);
+}
+
+void
+cserve2_shared_mempool_del(Shared_Mempool *sm)
+{
+   if (!sm) return;
+   eina_rbtree_delete(EINA_RBTREE_GET(sm->empty_blocks),
+                      EINA_RBTREE_FREE_CB(_shared_mempool_block_del), sm);
+   _shared_data_shm_del(sm->ds);
+   _shared_index_del(sm->index);
+   free(sm);
+}
+
+static Index_Entry *
+_shared_mempool_buffer_new(Shared_Mempool *sm, int size)
+{
+
+   Index_Entry *ie;
+   Block *blk;
+   int mapping_size, new_mapping_size;
+   int rsize = _data_blocksize_roundup(size);
+
+   if (!sm) return NULL;
+   if (size <= 0) return NULL;
+
+   mapping_size = cserve2_shm_map_size_get(sm->ds->shm);
+
+   ie = _shared_index_entry_new(sm->index);
+   if (!ie) return NULL;
+
+   // Append if possible
+   if (rsize <= sm->empty_size)
+     {
+        ie->length = rsize;
+        ie->offset = mapping_size - sm->empty_size;
+        ie->shmid = cserve2_shm_id_get(sm->ds->shm);
+        ie->refcount = 1;
+        sm->empty_size -= rsize;
+        return ie;
+     }
+
+   // Find empty spot
+   blk = (Block *) eina_rbtree_inline_lookup(
+            EINA_RBTREE_GET(sm->empty_blocks), (void *) (intptr_t) rsize, 0,
+            EINA_RBTREE_CMP_KEY_CB(_block_rbtree_empty_spot_find), NULL);
+   if (blk && blk->length >= rsize)
+     {
+        ie->length = blk->length;
+        ie->offset = blk->offset;
+        ie->shmid = cserve2_shm_id_get(sm->ds->shm);
+        ie->refcount = 1;
+
+        sm->empty_blocks = (Block *) eina_rbtree_inline_remove(
+                 EINA_RBTREE_GET(sm->empty_blocks), EINA_RBTREE_GET(blk),
+                 EINA_RBTREE_CMP_NODE_CB(_block_rbtree_cmp), NULL);
+        if (blk->length == rsize)
+          free(blk);
+        else
+          {
+             blk->length -= rsize;
+             blk->offset += rsize;
+             sm->empty_blocks = (Block *) eina_rbtree_inline_insert(
+                      EINA_RBTREE_GET(sm->empty_blocks),  EINA_RBTREE_GET(blk),
+                      EINA_RBTREE_CMP_NODE_CB(_block_rbtree_cmp), NULL);
+          }
+
+        return ie;
+     }
+
+   // Grow mempool
+   new_mapping_size = _shared_data_shm_resize(
+            sm->ds, mapping_size + rsize - sm->empty_size);
+   if (new_mapping_size < 0)
+     {
+        memset(ie, 0, sizeof(Index_Entry));
+        return NULL;
+     }
+   ie->length = rsize;
+   ie->offset = mapping_size - sm->empty_size;
+   ie->shmid = cserve2_shm_id_get(sm->ds->shm);
+   ie->refcount = 1;
+   sm->empty_size += (new_mapping_size - mapping_size) - rsize;
+   return ie;
+}
+
+int
+cserve2_shared_mempool_buffer_new(Shared_Mempool *sm, int size)
+{
+   Index_Entry *ie;
+
+   ie = _shared_mempool_buffer_new(sm, size);
+   if (!ie) return -1;
+
+   return ie->id;
+}
+
+int
+cserve2_shared_mempool_buffer_ref(Shared_Mempool *sm, int bufferid)
+{
+   Index_Entry *ie;
+
+   if (!sm) return -1;
+   ie = _shared_index_entry_find(sm->index, bufferid);
+   if (!ie) return -1;
+
+   if (!ie->refcount)
+     {
+        Block *blk = (Block *)
+              eina_rbtree_inline_lookup(EINA_RBTREE_GET(sm->empty_blocks),
+                                        ie, sizeof(Index_Entry),
+                                        EINA_RBTREE_CMP_KEY_CB(
+                                           _block_rbtree_block_find),
+                                        sm);
+        if (blk && (blk->length == ie->length)
+            && (blk->offset == ie->offset)
+            && (blk->shmid == ie->shmid))
+          {
+             sm->empty_blocks = (Block *) eina_rbtree_inline_remove(
+                      EINA_RBTREE_GET(sm->empty_blocks), EINA_RBTREE_GET(blk),
+                      EINA_RBTREE_CMP_NODE_CB(_block_rbtree_cmp), NULL);
+             free(blk);
+          }
+     }
+
+   ie->refcount++;
+   return ie->id;
+}
+
+void
+cserve2_shared_mempool_buffer_del(Shared_Mempool *sm, int bufferid)
+{
+   Index_Entry *ie;
+
+   if (!sm || !bufferid) return;
+   ie = _shared_index_entry_find(sm->index, bufferid);
+   if (!ie || ie->refcount <= 0)
+     {
+        CRIT("Tried to delete invalid buffer or with refcount 0");
+        return;
+     }
+
+   ie->refcount--;
+   if (!ie->refcount)
+     {
+        Block *newblk = calloc(1, sizeof(Block));
+        newblk->length = ie->length;
+        newblk->offset = ie->offset;
+        newblk->shmid = ie->shmid;
+        sm->empty_blocks = (Block *) eina_rbtree_inline_insert(
+                 EINA_RBTREE_GET(sm->empty_blocks), EINA_RBTREE_GET(newblk),
+                 EINA_RBTREE_CMP_NODE_CB(_block_rbtree_cmp), NULL);
+     }
+}
+
+void *
+cserve2_shared_mempool_buffer_get(Shared_Mempool *sm, int bufferid)
+{
+   Index_Entry *ie;
+   char *data;
+
+   if (!sm) return NULL;
+   ie = _shared_index_entry_find(sm->index, bufferid);
+   if (!ie || ie->refcount <= 0)
+     {
+        CRIT("Tried to access invalid buffer or with refcount 0");
+        return NULL;
+     }
+
+   data = sm->ds->data + ie->offset;
+   return data;
+}
+
+
+// Shared strings
+
+int
+cserve2_shared_string_add(const char *str)
+{
+   Index_Entry *ie;
+   char *data;
+   int len, id;
+
+   if (!str) return -1;
+
+   // Find in known strings
+   id = (int) (intptr_t) eina_hash_find(_string_entries, str);
+   if (id > 0)
+     {
+        ie = _shared_index_entry_find(_string_mempool->index, id);
+        if (!ie || ie->refcount <= 0)
+          {
+             CRIT("String found in hash but not in mempool!");
+             eina_hash_del(_string_entries, str, (void *) (intptr_t) id);
+             goto new_entry;
+          }
+        ie->refcount++;
+        return ie->id;
+     }
+
+   // Add new entry
+new_entry:
+   len = strlen(str) + 1;
+   ie = _shared_mempool_buffer_new(_string_mempool, len);
+   if (!ie)
+     {
+        ERR("Could not store new string in shm");
+        return -1;
+     }
+
+   eina_hash_add(_string_entries, str, (void *) (intptr_t) ie->id);
+   data = _string_mempool->ds->data + ie->offset;
+   memcpy(data, str, len);
+   return ie->id;
+}
+
+int
+cserve2_shared_string_ref(int id)
+{
+   if (!id) return 0;
+   return cserve2_shared_mempool_buffer_ref(_string_mempool, id);
+}
+
+void
+cserve2_shared_string_del(int id)
+{
+   if (!id) return;
+   if (_shared_mempool_buffer_del(_string_mempool, id))
+     {
+        if (!eina_hash_del_by_data(_string_entries, (void *) (intptr_t) id))
+          CRIT("Invalid free");
+     }
+}
+
+const char *
+cserve2_shared_string_get(int id)
+{
+   if (!id) return NULL;
+   return cserve2_shared_mempool_buffer_get(_string_mempool, id);
+}
+
+
+
+// Init/destroy
+
+void
+cserve2_shared_index_init(void)
+{
+   if (!_instances)
+     {
+        DBG("Initializing shared index");
+        _string_mempool = cserve2_shared_mempool_new(4096);
+        _string_entries = eina_hash_string_djb2_new(NULL);
+     }
+   _instances++;
+}
+
+void
+cserve2_shared_index_shutdown(void)
+{
+   _instances--;
+   if (!_instances)
+     {
+        DBG("Destroying shared index");
+        cserve2_shared_mempool_del(_string_mempool);
+        eina_hash_free(_string_entries);
+
+        _string_mempool = NULL;
+        _string_entries = NULL;
+     }
+}
+
index a2bb9c4..e77eba2 100644 (file)
@@ -327,34 +327,26 @@ main(int argc EINA_UNUSED, const char *argv[])
         goto error;
      }
 
+   cserve2_shm_init();
+   cserve2_shared_index_init();
    cserve2_requests_init();
-
    cserve2_scale_init();
-
    cserve2_font_init();
-
    cserve2_cache_init();
-
-   cserve2_shm_init();
-
    _clients_setup();
 
    cserve2_main_loop_run();
 
    _clients_finish();
-
    cserve2_cache_shutdown();
-
    cserve2_font_shutdown();
-
    cserve2_scale_shutdown();
-
    cserve2_requests_shutdown();
-
    cserve2_slaves_shutdown();
 
    cserve2_main_loop_finish();
 
+   cserve2_shared_index_shutdown();
    cserve2_shm_shutdown();
 
    eina_prefix_free(_evas_cserve2_pfx);