widen the name field in database to accommodate longer strings
[profile/ivi/murphy.git] / src / resource / resource-owner.c
index 471aaa6..9269593 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
+#include <errno.h>
 
 #include <murphy/common/mm.h>
 #include <murphy/common/hashtbl.h>
 #include <murphy/common/utils.h>
 #include <murphy/common/log.h>
 
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/client-api.h>
 #include <murphy/resource/config-api.h>
 
 #include "resource-owner.h"
-#include "resource-class.h"
+#include "application-class.h"
 #include "resource-set.h"
 #include "resource.h"
 #include "zone.h"
+#include "resource-lua.h"
+
+#define NAME_LENGTH          24
+#define ATTR_LENGTH          64
 
+#define ZONE_ID_IDX          0
+#define ZONE_NAME_IDX        1
+#define CLASS_NAME_IDX       2
+#define RSET_ID_IDX          3
+#define FIRST_ATTRIBUTE_IDX  4
 
-#define RESOURCE_MAX   (sizeof(mrp_resource_mask_t) * 8)
+typedef struct {
+    uint32_t          zone_id;
+    const char       *zone_name;
+    const char       *class_name;
+    uint32_t          rset_id;
+    mrp_attr_value_t  attrs[MQI_COLUMN_MAX];
+} owner_row_t;
 
-static mrp_resource_owner_t  resource_owners[MRP_ZONE_MAX * RESOURCE_MAX];
+static mrp_resource_owner_t  resource_owners[MRP_ZONE_MAX * MRP_RESOURCE_MAX];
+static mqi_handle_t          owner_tables[MRP_RESOURCE_MAX];
 
 static mrp_resource_owner_t *get_owner(uint32_t, uint32_t);
 static void reset_owners(uint32_t, mrp_resource_owner_t *);
 static bool grant_ownership(mrp_resource_owner_t *, mrp_zone_t *,
-                            mrp_resource_class_t *, mrp_resource_set_t *,
+                            mrp_application_class_t *, mrp_resource_set_t *,
                             mrp_resource_t *);
 static bool advice_ownership(mrp_resource_owner_t *, mrp_zone_t *,
-                             mrp_resource_class_t *, mrp_resource_set_t *,
+                             mrp_application_class_t *, mrp_resource_set_t *,
                              mrp_resource_t *);
 
 static void manager_start_transaction(mrp_zone_t *);
 static void manager_end_transaction(mrp_zone_t *);
 
+static void delete_resource_owner(mrp_zone_t *, mrp_resource_t *);
+static void insert_resource_owner(mrp_zone_t *, mrp_application_class_t *,
+                                  mrp_resource_set_t *, mrp_resource_t *);
+static void update_resource_owner(mrp_zone_t *, mrp_application_class_t *,
+                                  mrp_resource_set_t *, mrp_resource_t *);
+static void set_attr_descriptors(mqi_column_desc_t *, mrp_resource_t *);
 
 
-void mrp_resource_owner_update_zone(uint32_t zoneid)
+int mrp_resource_owner_create_database_table(mrp_resource_def_t *rdef)
 {
-    mrp_resource_owner_t oldowners[RESOURCE_MAX];
-    mrp_resource_owner_t backup[RESOURCE_MAX];
+    MQI_COLUMN_DEFINITION_LIST(base_coldefs,
+        MQI_COLUMN_DEFINITION( "zone_id"          , MQI_UNSIGNED             ),
+        MQI_COLUMN_DEFINITION( "zone_name"        , MQI_VARCHAR(NAME_LENGTH) ),
+        MQI_COLUMN_DEFINITION( "application_class", MQI_VARCHAR(NAME_LENGTH) ),
+        MQI_COLUMN_DEFINITION( "resource_set_id"  , MQI_UNSIGNED             )
+    );
+
+    MQI_INDEX_DEFINITION(indexdef,
+        MQI_INDEX_COLUMN( "zone_id" )
+    );
+
+    static bool initialized = false;
+
+    char name[256];
+    mqi_column_def_t coldefs[MQI_COLUMN_MAX + 1];
+    mqi_column_def_t *col;
+    mrp_attr_def_t *atd;
+    mqi_handle_t table;
+    char c, *p;
+    size_t i,j;
+
+    if (!initialized) {
+        mqi_open();
+        for (i = 0;  i < MRP_RESOURCE_MAX;  i++)
+            owner_tables[i] = MQI_HANDLE_INVALID;
+        initialized = true;
+    }
+
+    MRP_ASSERT(sizeof(base_coldefs) < sizeof(coldefs),"too many base columns");
+    MRP_ASSERT(rdef, "invalid argument");
+    MRP_ASSERT(rdef->id < MRP_RESOURCE_MAX, "confused with data structures");
+    MRP_ASSERT(owner_tables[rdef->id] == MQI_HANDLE_INVALID,
+               "owner table already exist");
+
+    snprintf(name, sizeof(name), "%s_owner", rdef->name);
+    for (p = name; (c = *p);  p++) {
+        if (!isascii(c) || (!isalnum(c) && c != '_'))
+            *p = '_';
+    }
+
+    j = MQI_DIMENSION(base_coldefs) - 1;
+    memcpy(coldefs, base_coldefs, j * sizeof(mqi_column_def_t));
+
+    for (i = 0;  i < rdef->nattr && j < MQI_COLUMN_MAX;  i++, j++) {
+        col = coldefs + j;
+        atd = rdef->attrdefs + i;
+
+        col->name   = atd->name;
+        col->type   = atd->type;
+        col->length = (col->type == mqi_string) ? ATTR_LENGTH : 0;
+        col->flags  = 0;
+    }
+
+    memset(coldefs + j, 0, sizeof(mqi_column_def_t));
+
+    table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
+
+    if (table == MQI_HANDLE_INVALID) {
+        mrp_log_error("Can't create table '%s': %s", name, strerror(errno));
+        return -1;
+    }
+
+    owner_tables[rdef->id] = table;
+
+    return 0;
+}
+
+void mrp_resource_owner_recalc(uint32_t zoneid)
+{
+    mrp_resource_owner_update_zone(zoneid, NULL, 0);
+}
+
+void mrp_resource_owner_update_zone(uint32_t zoneid,
+                                    mrp_resource_set_t *reqset,
+                                    uint32_t reqid)
+{
+    typedef struct {
+        uint32_t replyid;
+        mrp_resource_set_t *rset;
+        bool move;
+    } event_t;
+
+    mrp_resource_owner_t oldowners[MRP_RESOURCE_MAX];
+    mrp_resource_owner_t backup[MRP_RESOURCE_MAX];
     mrp_zone_t *zone;
-    mrp_resource_class_t *class;
+    mrp_application_class_t *class;
     mrp_resource_set_t *rset;
     mrp_resource_t *res;
     mrp_resource_def_t *rdef;
     mrp_resource_mgr_ftbl_t *ftbl;
-    mrp_resource_owner_t *owner, *old;
+    mrp_resource_owner_t *owner, *old, *owners;
     mrp_resource_mask_t mask;
     mrp_resource_mask_t mandatory;
     mrp_resource_mask_t grant;
@@ -80,6 +190,14 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
     void *clc, *rsc, *rc;
     uint32_t rid;
     uint32_t rcnt;
+    bool force_release;
+    bool changed;
+    bool move;
+    mrp_resource_event_t notify;
+    uint32_t replyid;
+    uint32_t nevent, maxev;
+    event_t *events, *ev, *lastev;
+    mqi_handle_t trans_handle;
 
     MRP_ASSERT(zoneid < MRP_ZONE_MAX, "invalid argument");
 
@@ -87,23 +205,31 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
 
     MRP_ASSERT(zone, "zone is not defined");
 
+    if (!(maxev = mrp_get_resource_set_count()))
+        return;
+
+    nevent = 0;
+    events = mrp_alloc(sizeof(event_t) * maxev);
+
+    MRP_ASSERT(events, "Memory alloc failure. Can't update zone");
+
     reset_owners(zoneid, oldowners);
     manager_start_transaction(zone);
 
-
     rcnt = mrp_resource_definition_count();
     clc  = NULL;
 
-    while ((class = mrp_resource_class_iterate_classes(&clc))) {
+    while ((class = mrp_application_class_iterate_classes(&clc))) {
         rsc = NULL;
 
-        while ((rset = mrp_resource_class_iterate_rsets(class,zoneid,&rsc))) {
+        while ((rset=mrp_application_class_iterate_rsets(class,zoneid,&rsc))) {
+            force_release = false;
             mandatory = rset->resource.mask.mandatory;
             grant = 0;
             advice = 0;
             rc = NULL;
 
-            switch (rset->request.type) {
+            switch (rset->state) {
 
             case mrp_resource_acquire:
                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
@@ -115,17 +241,25 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
 
                     if (grant_ownership(owner, zone, class, rset, res))
                         grant |= ((mrp_resource_mask_t)1 << rid);
+                    else {
+                        if (owner->rset != rset)
+                            force_release |= owner->modal;
+                    }
                 }
-                if (mandatory && (grant & mandatory) == mandatory)
+                owners = get_owner(zoneid, 0);
+                if ((grant & mandatory) == mandatory &&
+                    mrp_resource_lua_veto(zone, rset, owners, grant, reqset))
+                {
                     advice = grant;
+                }
                 else {
                     /* rollback, ie. restore the backed up state */
                     rc = NULL;
                     while ((res=mrp_resource_set_iterate_resources(rset,&rc))){
-                         rdef  = res->def;
-                         rid   = rdef->id;
-                         mask  = (mrp_resource_mask_t)1 << rid;
-                         owner = get_owner(zoneid, rid);
+                        rdef = res->def;
+                        rid = rdef->id;
+                        mask = (mrp_resource_mask_t)1 << rid;
+                        owner = get_owner(zoneid, rid);
                         *owner = backup[rid];
 
                         if ((grant & mask)) {
@@ -137,8 +271,12 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
                             advice |= mask;
                     }
 
-                    /* nothing is granted */
                     grant = 0;
+
+                    if ((advice & mandatory) != mandatory)
+                        advice = 0;
+
+                    mrp_resource_lua_set_owners(zone, owners);
                 }
                 break;
 
@@ -149,9 +287,9 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
                     owner = get_owner(zoneid, rid);
 
                     if (advice_ownership(owner, zone, class, rset, res))
-                        advice |= ((uint32_t)1 << rid);
+                        advice |= ((mrp_resource_mask_t)1 << rid);
                 }
-                if (mandatory && (advice & mandatory) != mandatory)
+                if ((advice & mandatory) != mandatory)
                     advice = 0;
                 break;
 
@@ -159,19 +297,94 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
                 break;
             }
 
-            if (grant != rset->resource.mask.grant) {
-                rset->resource.mask.grant = grant;
+            changed = false;
+            move    = false;
+            notify  = 0;
+            replyid = (reqset == rset && reqid == rset->request.id) ? reqid:0;
+
+
+            if (force_release) {
+                move = (rset->state != mrp_resource_release);
+                notify = move ? MRP_RESOURCE_EVENT_RELEASE : 0;
+                changed = move || rset->resource.mask.grant;
+                rset->state = mrp_resource_release;
+                rset->resource.mask.grant = 0;
+            }
+            else {
+                if (grant == rset->resource.mask.grant) {
+                    if (rset->state == mrp_resource_acquire &&
+                        !grant && rset->dont_wait.current)
+                    {
+                        rset->state = mrp_resource_release;
+                        rset->dont_wait.current = rset->dont_wait.client;
+
+                        notify = MRP_RESOURCE_EVENT_RELEASE;
+                        move = true;
+                    }
+                }
+                else {
+                    rset->resource.mask.grant = grant;
+                    changed = true;
+
+                    if (rset->state != mrp_resource_release &&
+                        !grant && rset->auto_release.current)
+                    {
+                        rset->state = mrp_resource_release;
+                        rset->auto_release.current = rset->auto_release.client;
+
+                        notify = MRP_RESOURCE_EVENT_RELEASE;
+                        move = true;
+                    }
+                }
+            }
+
+            if (notify) {
+                mrp_resource_set_notify(rset, notify);
             }
 
             if (advice != rset->resource.mask.advice) {
                 rset->resource.mask.advice = advice;
+                changed = true;
             }
 
+            if (replyid || changed) {
+                ev = events + nevent++;
+
+                ev->replyid = replyid;
+                ev->rset    = rset;
+                ev->move    = move;
+            }
         } /* while rset */
     } /* while class */
 
     manager_end_transaction(zone);
 
+    for (lastev = (ev = events) + nevent;     ev < lastev;     ev++) {
+        rset = ev->rset;
+
+        if (ev->move)
+            mrp_application_class_move_resource_set(rset);
+
+        mrp_resource_set_updated(rset);
+
+        /* first we send out the revoke/deny events
+         * followed by the grants (in the next for loop)
+         */
+        if (rset->event && !rset->resource.mask.grant)
+            rset->event(ev->replyid, rset, rset->user_data);
+    }
+
+    for (lastev = (ev = events) + nevent;     ev < lastev;     ev++) {
+        rset = ev->rset;
+
+        if (rset->event && rset->resource.mask.grant)
+            rset->event(ev->replyid, rset, rset->user_data);
+    }
+
+    mrp_free(events);
+
+    trans_handle = MQI_HANDLE_INVALID;
+
     for (rid = 0;  rid < rcnt;  rid++) {
         owner = get_owner(zoneid, rid);
         old   = oldowners + rid;
@@ -180,9 +393,20 @@ void mrp_resource_owner_update_zone(uint32_t zoneid)
             owner->rset  != old->rset  ||
             owner->res   != old->res     )
         {
-
+            if (trans_handle == MQI_HANDLE_INVALID)
+                trans_handle = MQI_BEGIN;
+
+            if (!owner->res)
+               delete_resource_owner(zone,old->res);
+            else if (!old->res)
+               insert_resource_owner(zone,owner->class,owner->rset,owner->res);
+            else
+               update_resource_owner(zone,owner->class,owner->rset,owner->res);
         }
     }
+
+    if (trans_handle != MQI_HANDLE_INVALID)
+        MQI_COMMIT(trans_handle);
 }
 
 int mrp_resource_owner_print(char *buf, int len)
@@ -191,7 +415,7 @@ int mrp_resource_owner_print(char *buf, int len)
 
     mrp_zone_t *zone;
     mrp_resource_owner_t *owner;
-    mrp_resource_class_t *class;
+    mrp_application_class_t *class;
     mrp_resource_set_t *rset;
     mrp_resource_t *res;
     mrp_resource_def_t *rdef;
@@ -199,7 +423,10 @@ int mrp_resource_owner_print(char *buf, int len)
     uint32_t zcnt, zid;
     char *p, *e;
 
-    MRP_ASSERT(buf && len > 0, "invalid argument");
+    if (len <= 0)
+        return 0;
+
+    MRP_ASSERT(buf, "invalid argument");
 
     rcnt = mrp_resource_definition_count();
     zcnt = mrp_zone_count();
@@ -254,27 +481,32 @@ int mrp_resource_owner_print(char *buf, int len)
 
 static mrp_resource_owner_t *get_owner(uint32_t zone, uint32_t resid)
 {
-    MRP_ASSERT(zone < MRP_ZONE_MAX && resid < RESOURCE_MAX,"invalid argument");
+    MRP_ASSERT(zone < MRP_ZONE_MAX && resid < MRP_RESOURCE_MAX,
+               "invalid argument");
 
-    return resource_owners + (zone * RESOURCE_MAX + resid);
+    return resource_owners + (zone * MRP_RESOURCE_MAX + resid);
 }
 
 static void reset_owners(uint32_t zone, mrp_resource_owner_t *oldowners)
 {
-    void   *ptr  = get_owner(zone, 0);
-    size_t  size = sizeof(mrp_resource_owner_t) * RESOURCE_MAX;
+    mrp_resource_owner_t *owners = get_owner(zone, 0);
+    size_t size = sizeof(mrp_resource_owner_t) * MRP_RESOURCE_MAX;
+    size_t i;
 
     if (oldowners)
-        memcpy(oldowners, ptr, size);
+        memcpy(oldowners, owners, size);
+
+    memset(owners, 0, size);
 
-    memset(ptr, 0, size);
+    for (i = 0;   i < MRP_RESOURCE_MAX;   i++)
+        owners[i].share = true;
 }
 
-static bool grant_ownership(mrp_resource_owner_t *owner,
-                            mrp_zone_t           *zone,
-                            mrp_resource_class_t *class,
-                            mrp_resource_set_t   *rset,
-                            mrp_resource_t       *res)
+static bool grant_ownership(mrp_resource_owner_t    *owner,
+                            mrp_zone_t              *zone,
+                            mrp_application_class_t *class,
+                            mrp_resource_set_t      *rset,
+                            mrp_resource_t          *res)
 {
     mrp_resource_def_t      *rdef = res->def;
     mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
@@ -285,6 +517,9 @@ static bool grant_ownership(mrp_resource_owner_t *owner,
         return false;
      */
 
+    if (owner->modal)
+        return false;
+
     do { /* not a loop */
         if (!owner->class && !owner->rset) {
             /* nobody owns this, so grab it */
@@ -297,10 +532,9 @@ static bool grant_ownership(mrp_resource_owner_t *owner,
             break;
         }
 
-        if (owner->share) {
-            /* OK, someone else owns it bu
+        if (rdef->shareable && owner->share) {
+            /* OK, someone else owns it but
                the owner is ready to share it with us */
-            owner->share = res->shared;
             break;
         }
 
@@ -317,17 +551,19 @@ static bool grant_ownership(mrp_resource_owner_t *owner,
         owner->class = class;
         owner->rset  = rset;
         owner->res   = res;
-        owner->share = res->shared;
+        owner->modal = class->modal;
     }
 
+    owner->share = class->share && res->shared;
+
     return true;
 }
 
-static bool advice_ownership(mrp_resource_owner_t *owner,
-                             mrp_zone_t           *zone,
-                             mrp_resource_class_t *class,
-                             mrp_resource_set_t   *rset,
-                             mrp_resource_t       *res)
+static bool advice_ownership(mrp_resource_owner_t    *owner,
+                             mrp_zone_t              *zone,
+                             mrp_application_class_t *class,
+                             mrp_resource_set_t      *rset,
+                             mrp_resource_t          *res)
 {
     mrp_resource_def_t      *rdef = res->def;
     mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
@@ -339,6 +575,9 @@ static bool advice_ownership(mrp_resource_owner_t *owner,
         return false;
      */
 
+    if (owner->modal)
+        return false;
+
     do { /* not a loop */
         if (!owner->class && !owner->rset)
             /* nobody owns this */
@@ -348,9 +587,10 @@ static bool advice_ownership(mrp_resource_owner_t *owner,
             /* someone else owns it but it can be shared */
             break;
 
-
         if (owner->class == class) {
-            if (owner->rset->class.priority == rset->class.priority)
+            if (owner->rset->class.priority == rset->class.priority &&
+                    class->order == MRP_RESOURCE_ORDER_LIFO)
+                /* same class and resource goes to the last one who asks it */
                 break;
         }
 
@@ -399,6 +639,139 @@ static void manager_end_transaction(mrp_zone_t *zone)
 }
 
 
+static void delete_resource_owner(mrp_zone_t *zone, mrp_resource_t *res)
+{
+    static uint32_t zone_id;
+
+    MQI_WHERE_CLAUSE(where,
+        MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
+    );
+
+    mrp_resource_def_t *rdef;
+    int n;
+
+    MRP_ASSERT(res, "invalid argument");
+
+    rdef = res->def;
+    zone_id = zone->id;
+
+    if ((n = MQI_DELETE(owner_tables[rdef->id], where)) != 1)
+        mrp_log_error("Could not delete resource owner");
+}
+
+static void insert_resource_owner(mrp_zone_t *zone,
+                                  mrp_application_class_t *class,
+                                  mrp_resource_set_t *rset,
+                                  mrp_resource_t *res)
+{
+    mrp_resource_def_t *rdef = res->def;
+    uint32_t i;
+    int n;
+    owner_row_t row;
+    owner_row_t *rows[2];
+    mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+    MRP_ASSERT(FIRST_ATTRIBUTE_IDX + rdef->nattr <= MQI_COLUMN_MAX,
+               "too many attributes for a table");
+
+    row.zone_id    = zone->id;
+    row.zone_name  = zone->name;
+    row.class_name = class->name;
+    row.rset_id    = rset->id;
+    memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
+
+    i = 0;
+    cdsc[i].cindex = ZONE_ID_IDX;
+    cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_id);
+
+    i++;
+    cdsc[i].cindex = ZONE_NAME_IDX;
+    cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_name);
+
+    i++;
+    cdsc[i].cindex = CLASS_NAME_IDX;
+    cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
+
+    i++;
+    cdsc[i].cindex = RSET_ID_IDX;
+    cdsc[i].offset = MQI_OFFSET(owner_row_t, rset_id);
+    
+    set_attr_descriptors(cdsc + (i+1), res);
+
+    rows[0] = &row;
+    rows[1] = NULL;
+
+    if ((n = MQI_INSERT_INTO(owner_tables[rdef->id], cdsc, rows)) != 1)
+        mrp_log_error("can't insert row into owner table");
+}
+
+static void update_resource_owner(mrp_zone_t *zone,
+                                  mrp_application_class_t *class,
+                                  mrp_resource_set_t *rset,
+                                  mrp_resource_t *res)
+{
+    static uint32_t zone_id;
+
+    MQI_WHERE_CLAUSE(where,
+        MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
+    );
+
+    mrp_resource_def_t *rdef = res->def;
+    uint32_t i;
+    int n;
+    owner_row_t row;
+    mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+    zone_id = zone->id;
+
+    MRP_ASSERT(1 + rdef->nattr <= MQI_COLUMN_MAX,
+               "too many attributes for a table");
+
+    row.class_name = class->name;
+    row.rset_id    = rset->id;
+    memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
+
+    i = 0;
+    cdsc[i].cindex = CLASS_NAME_IDX;
+    cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
+
+    i++;
+    cdsc[i].cindex = RSET_ID_IDX;
+    cdsc[i].offset = MQI_OFFSET(owner_row_t, rset_id);
+
+    set_attr_descriptors(cdsc + (i+1), res);
+
+
+    if ((n = MQI_UPDATE(owner_tables[rdef->id], cdsc, &row, where)) != 1)
+        mrp_log_error("can't update row in owner table");
+}
+
+
+static void set_attr_descriptors(mqi_column_desc_t *cdsc, mrp_resource_t *res)
+{
+    mrp_resource_def_t *rdef = res->def;
+    uint32_t i,j;
+    int o;
+
+    for (i = j = 0;  j < rdef->nattr;  j++) {
+        switch (rdef->attrdefs[j].type) {
+        case mqi_string:   o = MQI_OFFSET(owner_row_t,attrs[j].string);  break;
+        case mqi_integer:  o = MQI_OFFSET(owner_row_t,attrs[j].integer); break;
+        case mqi_unsignd:  o = MQI_OFFSET(owner_row_t,attrs[j].unsignd); break;
+        case mqi_floating: o = MQI_OFFSET(owner_row_t,attrs[j].floating);break;
+        default:           /* skip this */                            continue;
+        }
+
+        cdsc[i].cindex = FIRST_ATTRIBUTE_IDX + j;
+        cdsc[i].offset = o;
+        i++;
+    }
+
+    cdsc[i].cindex = -1;
+    cdsc[i].offset =  1;
+}
+
+
 /*
  * Local Variables:
  * c-basic-offset: 4