summaryrefslogblamecommitdiff
path: root/src/bin/evas/evas_cserve2_index.c
blob: 42bcffad37cacdb72d134a83f0496e0715ce4559 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                  
                            















                                                           





























                                                    




                                                                          


                                                   



















































































                                                                         
                 
                                                 









                                    
                                                               
 
                                                      



















                                                                             
           






                                  
          







                                                     
                                                                     


























                                                                  
                                                                                 












                                                                 

                                                                           
                                                    









                                                                               
                                             
                            
                             
                         
                                                           

































                                                






                                                   












                                                        









                                                                               






                                                                            

                                                                           


































































                                                                                
                                                                
                                                             

                                                














                                                        
                                                




                                                  
                                                                                      
            



                                                                







                                                         
                            



                                    
                                     












                                                            





















                                                             

                        


                                                                               















                                                                 

                  























                                                         
                                                                
 



                                           
 











                                                                          





                                                       



                         

      

                     
                     


                                                        






                          
 

                                                             
      
                                                        



                          





                     
                                                                 


                    
 

                                                              


                                        



                                                                            





























                                         

                                                            



                       

                                                              




                                          
                                                                               
 
                                                          





                    
                                                                          










                                     






                                                    










































































































                                                                               
                                                           
























                                                                              
                   
                                                            

                   
                    
 
                                     
 
                                                           

                                
                                                                 
                    











                                                                            

                                         
      
 
               





                                                                   








                                                                   
                                                           

                                
                                                                 






                                    








                                                                          
                                                                 





                     



                                                   

                                                            








                                                   

















                                                                               


                 

                                           

















                                                                    






                                          
                      




                                                              
                                                                       

                                     
                                                             













                                                                         
                 

      

                                                 
                                                                    





                                 
                   
                         



                                                                  





                                                                 

                    
                       
                                                                        
      


                                                                                 
                                   
           
      
                                 




                                 
                            


                                                                 




























                                                                           








                               


                                              
                                         
                                                                                      
                                                          


                                                
                                        


















                                                    
/* 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 _Block Block;
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
{
   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;
};


// 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

// Recommended minimum size for arrays and mempools
#define ARRAY_MINSIZE (32 * 1024)

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

static Data_Shm *
_shared_data_shm_new(const char *infix, 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, 0);

   ds->shm = cserve2_shm_request(infix, 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;
}

static void
_shared_data_shm_del(Data_Shm *ds)
{
   if (!ds) return;
   cserve2_shm_unref(ds->shm);
   free(ds);
}

static 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, ARRAY_MINSIZE);
   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 generation_id, 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),
                                             ARRAY_MINSIZE);
   ds = _shared_data_shm_new("array", 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 = generation_id;
   sa->header->emptyidx = 0;
   sa->header->sortedidx = 0;
   sa->header->tag = tag;
   memset(&sa->header->_reserved1, 0, sizeof(int32_t) * 2);

   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_map_size_get(Shared_Array *sa)
{
   if (!sa || !sa->ds) return 0;
   return cserve2_shm_map_size_get(sa->ds->shm);
}

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_generation_id_set(Shared_Array *sa, int generation_id)
{
   if (!sa) return -1;
   if (sa->header->generation_id == generation_id)
     return 0;

   sa->header->generation_id = generation_id;
   return 1;
}

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),
                                             ARRAY_MINSIZE);
   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, int generation_id,
                            Shared_Array_Repack_Skip_Cb skip,
                            Eina_Compare_Cb cmp,
                            void *user_data)
{
   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, user_data)) continue;
        l = eina_list_sorted_insert(l, cmp, data);
        newcount++;
     }

   // Create new array
   sa2 = cserve2_shared_array_new(sa->header->tag, generation_id, elemsize, newcount);
   if (!sa2)
     {
        ERR("Can not repack array: failed to create new array");
        return NULL;
     }

   // 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);
        dstdata += elemsize;
     }

   // Finalize & return
   sa2->header->emptyidx = newcount;
   sa2->header->sortedidx = 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;

   // Binary search
   if (sa->header->sortedidx > 0)
     {
        int low = 0;
        int high = sa->header->sortedidx;
        int prev = -1;
        int r;
        k = high / 2;
        while (prev != k)
          {
             ptr = cserve2_shared_array_item_data_get(sa, k);
             r = cmp(ptr, data);
             if (!r)
               return k;
             else if (r > 0)
               high = k;
             else
               low = k;
             prev = k;
             k = low + (high - low) / 2;
          }
     }

   // Linear search O(n)
   k = sa->header->sortedidx;
   ptr = sa->ds->data + sizeof(Shared_Array_Header) + k * sa->header->elemsize;
   for (; 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_get_by_id(Shared_Index *si, unsigned int id)
{
   Index_Entry *obj;
   const char *base;
   int low = 0, high, start_high, elemsize;
   int cur;

   if (!si || !si->sa || !id)
     return NULL;

   // FIXME: HACK (consider all arrays always sorted by id)
   high = si->sa->header->emptyidx; // Should be si->sa->header->sortedidx

   if (high > si->sa->header->count)
     high = si->sa->header->count;

   base = si->sa->ds->data + sizeof(Shared_Array_Header);
   elemsize = si->sa->header->elemsize;

   // Direct access, works for non-repacked arrays
   if ((int) id < high)
     {
        obj = (Index_Entry *) (base + (elemsize * id));
        if (obj->id == id)
          return obj;
        if (obj->id < id)
          low = id + 1;
        else
          high = id;
     }

   // Binary search
   start_high = high;
   while (high > low)
     {
        cur = low + ((high - low) / 2);
        obj = (Index_Entry *) (base + (elemsize * cur));
        if (obj->id == id)
          return obj;
        if (obj->id < id)
          low = cur + 1;
        else
          high = cur;
     }

   // Linear search
   for (cur = start_high; cur < si->sa->header->count; cur++)
     {
        obj = (Index_Entry *) (base + (elemsize * cur));
        if (!obj->id)
          return NULL;
        if (obj->id == id)
          return obj;
     }

   return NULL;
}

static Shared_Index *
_shared_index_new(int tag, int index_elemsize, int generation_id)
{
   Shared_Index *si;
   Index_Entry *ie;

   EINA_SAFETY_ON_FALSE_RETURN_VAL(index_elemsize >= 0, NULL);

   si = calloc(1, sizeof(Shared_Index));
   if (!si) return NULL;

   if (!index_elemsize)
     index_elemsize = sizeof(Index_Entry);

   si->sa = cserve2_shared_array_new(tag, generation_id, index_elemsize, 0);
   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 indextag, int index_elemsize,
                           int generation_id, int initsize)
{
   Shared_Mempool *sm;
   size_t mapping_size;

   EINA_SAFETY_ON_FALSE_RETURN_VAL(initsize >= 0, NULL);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(index_elemsize >= 0, NULL);

   sm = calloc(1, sizeof(Shared_Mempool));
   if (!sm) return NULL;

   if (!initsize) initsize = 1;
   mapping_size = cserve2_shm_size_normalize((size_t) initsize, ARRAY_MINSIZE);

   sm->ds = _shared_data_shm_new("mempool", mapping_size);
   if (!sm->ds)
     {
        free(sm);
        return NULL;
     }

   sm->index = _shared_index_new(indextag, index_elemsize, generation_id);
   if (!sm->index)
     {
        _shared_data_shm_del(sm->ds);
        free(sm);
        return NULL;
     }

   sm->empty_size = mapping_size;
   return sm;
}

Shared_Array *
cserve2_shared_mempool_index_get(Shared_Mempool *sm)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(sm, NULL);
   return sm->index->sa;
}

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_get_by_id(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;
}

static const void *
_shared_mempool_buffer_del(Shared_Mempool *sm, int bufferid)
{
   Index_Entry *ie;
   const char *data;

   if (!sm || !bufferid) return NULL;

   ie = _shared_index_entry_get_by_id(sm->index, bufferid);
   if (!ie || ie->refcount <= 0)
     {
        CRI("Tried to delete invalid buffer or with refcount 0");
        return NULL;
     }

   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);
        data = sm->ds->data + ie->offset;
        return data;
     }

   return NULL;
}

void
cserve2_shared_mempool_buffer_del(Shared_Mempool *sm, int bufferid)
{
   _shared_mempool_buffer_del(sm, bufferid);
}

void *
cserve2_shared_mempool_buffer_get(Shared_Mempool *sm, int bufferid)
{
   Index_Entry *ie;
   char *data;

   if (!sm) return NULL;
   ie = _shared_index_entry_get_by_id(sm->index, bufferid);
   if (!ie || ie->refcount <= 0)
     {
        CRI("Tried to access invalid buffer or with refcount 0");
        return NULL;
     }

   data = sm->ds->data + ie->offset;
   return data;
}

int
cserve2_shared_mempool_buffer_offset_get(Shared_Mempool *sm, int bufferid)
{
   Index_Entry *ie;

   if (!sm) return -1;
   ie = _shared_index_entry_get_by_id(sm->index, bufferid);
   if (!ie || ie->refcount <= 0)
     {
        CRI("Tried to access invalid buffer or with refcount 0");
        return -1;
     }

   return ie->offset;
}

size_t
cserve2_shared_mempool_size_get(Shared_Mempool *sm)
{
   if (!sm) return 0;
   return cserve2_shm_map_size_get(sm->ds->shm)
         + cserve2_shared_array_map_size_get(sm->index->sa);
}

const char *
cserve2_shared_mempool_name_get(Shared_Mempool *sm)
{
   if (!sm) return NULL;
   return cserve2_shm_name_get(sm->ds->shm);
}

int
cserve2_shared_mempool_generation_id_get(Shared_Mempool *sm)
{
   if (!sm) return -1;
   return sm->index->sa->header->generation_id;
}

int
cserve2_shared_mempool_generation_id_set(Shared_Mempool *sm, int generation_id)
{
   if (!sm) return -1;
   if (sm->index->sa->header->generation_id == generation_id)
     return 0;

   sm->index->sa->header->generation_id = generation_id;
   return 1;
}


// Shared strings

static int _shared_strings_unref_items = 0;

const char *
cserve2_shared_strings_table_name_get()
{
   if (!_string_mempool)
     return NULL;

   return cserve2_shm_name_get(_string_mempool->ds->shm);
}

const char *
cserve2_shared_strings_index_name_get()
{
   if (!_string_mempool)
     return NULL;

   return cserve2_shared_array_name_get(_string_mempool->index->sa);
}

int
cserve2_shared_string_add(const char *str)
{
   Index_Entry *ie;
   char *data;
   int len, id;

   if (!str) return 0;

   // Find in known strings
   id = (int) (intptr_t) eina_hash_find(_string_entries, str);
   if (id > 0)
     {
        ie = _shared_index_entry_get_by_id(_string_mempool->index, id);
        if (!ie || ie->refcount <= 0)
          {
             ERR("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 0;
     }

   data = _string_mempool->ds->data + ie->offset;
   memcpy(data, str, len);
   eina_hash_add(_string_entries, data, (void *) (intptr_t) ie->id);
   return ie->id;
}

int
cserve2_shared_string_ref(int id)
{
   Index_Entry *ie;
   if (id <= 0) return 0;
   ie = _shared_index_entry_get_by_id(_string_mempool->index, id);
   if (!ie) return 0;
   if (!ie->refcount)
     _string_mempool--;
   return cserve2_shared_mempool_buffer_ref(_string_mempool, id);
}

void
cserve2_shared_string_del(int id)
{
   const char *data;

   if (id <= 0) return;
   if ((data = _shared_mempool_buffer_del(_string_mempool, id)) != NULL)
     {
        if (!eina_hash_del_by_key(_string_entries, data))
          {
             if (!eina_hash_del_by_data(_string_entries, (void *) (intptr_t) id))
               CRI("Invalid free");
          }
     }
   _shared_strings_unref_items++;
}

const char *
cserve2_shared_string_get(int id)
{
   if (id <= 0) return NULL;
   return cserve2_shared_mempool_buffer_get(_string_mempool, id);
}

int
cserve2_shared_strings_repack(Shared_Array_Repack_Skip_Cb skip,
                              Eina_Compare_Cb cmp)
{
   int count;

   if (!_string_mempool->index) return -1;
   count = _string_mempool->index->lastid;
   if (!count) return -1;
   if (_shared_strings_unref_items * 100 / count >= 25
       && _shared_strings_unref_items > 100)
     {
        Shared_Array *sa;
        int genid;

        genid = _string_mempool->index->sa->header->generation_id + 1;
        sa = cserve2_shared_array_repack(_string_mempool->index->sa, genid,
                                         skip, cmp, NULL);
        if (!sa) return -1;

        cserve2_shared_array_del(_string_mempool->index->sa);
        _string_mempool->index->sa = sa;
        _shared_strings_unref_items = 0;
        return 1;
     }

   return 0;
}



// Init/destroy

void
cserve2_shared_index_init(void)
{
   if (!_instances)
     {
        char faketag[5] = {0};
        int ifaketag = STRING_MEMPOOL_FAKETAG;

        DBG("Initializing shared index");
        _string_mempool = cserve2_shared_mempool_new(STRING_INDEX_ARRAY_TAG, 0, 0, 0);
        _string_entries = eina_hash_string_djb2_new(NULL);

        memcpy(faketag, &ifaketag, sizeof(int));
        cserve2_shared_string_add(faketag);
        _shared_strings_unref_items = 0;
     }
   _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;
     }
}