LCOV - code coverage report
Current view: top level - lib/dbwrap - dbwrap_rbt.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 191 251 76.1 %
Date: 2024-06-13 04:01:37 Functions: 13 18 72.2 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Database interface wrapper around red-black trees
       4             :    Copyright (C) Volker Lendecke 2007, 2008
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "dbwrap/dbwrap.h"
      22             : #include "dbwrap/dbwrap_private.h"
      23             : #include "dbwrap/dbwrap_rbt.h"
      24             : #include "../lib/util/rbtree.h"
      25             : #include "../lib/util/dlinklist.h"
      26             : 
      27             : #define DBWRAP_RBT_ALIGN(_size_) (((_size_)+15)&~15)
      28             : 
      29             : struct db_rbt_ctx {
      30             :         struct rb_root tree;
      31             :         struct db_rbt_node *nodes;
      32             :         size_t traverse_read;
      33             :         struct db_rbt_node **traverse_nextp;
      34             : };
      35             : 
      36             : struct db_rbt_rec {
      37             :         struct db_rbt_node *node;
      38             : };
      39             : 
      40             : /* The structure that ends up in the tree */
      41             : 
      42             : struct db_rbt_node {
      43             :         struct rb_node rb_node;
      44             :         size_t keysize, valuesize;
      45             :         struct db_rbt_node *prev, *next;
      46             : };
      47             : 
      48             : /*
      49             :  * Hide the ugly pointer calculations in a function
      50             :  */
      51             : 
      52    58009822 : static struct db_rbt_node *db_rbt2node(struct rb_node *node)
      53             : {
      54    58009822 :         return (struct db_rbt_node *)
      55             :                 ((char *)node - offsetof(struct db_rbt_node, rb_node));
      56             : }
      57             : 
      58             : /*
      59             :  * Compare two keys
      60             :  */
      61             : 
      62    58009822 : static int db_rbt_compare(TDB_DATA a, TDB_DATA b)
      63             : {
      64             :         int res;
      65             : 
      66    58009822 :         res = memcmp(a.dptr, b.dptr, MIN(a.dsize, b.dsize));
      67             : 
      68    58009822 :         if ((res < 0) || ((res == 0) && (a.dsize < b.dsize))) {
      69    24251912 :                 return -1;
      70             :         }
      71    33757910 :         if ((res > 0) || ((res == 0) && (a.dsize > b.dsize))) {
      72    24744046 :                 return 1;
      73             :         }
      74     9013864 :         return 0;
      75             : }
      76             : 
      77             : /*
      78             :  * dissect a db_rbt_node into its implicit key and value parts
      79             :  */
      80             : 
      81    59438341 : static void db_rbt_parse_node(struct db_rbt_node *node,
      82             :                               TDB_DATA *key, TDB_DATA *value)
      83             : {
      84             :         size_t key_offset, value_offset;
      85             : 
      86    59438341 :         key_offset = DBWRAP_RBT_ALIGN(sizeof(struct db_rbt_node));
      87    59438341 :         key->dptr = ((uint8_t *)node) + key_offset;
      88    59438341 :         key->dsize = node->keysize;
      89             : 
      90    59438341 :         value_offset = DBWRAP_RBT_ALIGN(node->keysize);
      91    59438341 :         value->dptr = key->dptr + value_offset;
      92    59438341 :         value->dsize = node->valuesize;
      93    59438341 : }
      94             : 
      95     1420447 : static ssize_t db_rbt_reclen(size_t keylen, size_t valuelen)
      96             : {
      97             :         size_t len, tmp;
      98             : 
      99     1420447 :         len = DBWRAP_RBT_ALIGN(sizeof(struct db_rbt_node));
     100             : 
     101     1420447 :         tmp = DBWRAP_RBT_ALIGN(keylen);
     102     1420447 :         if (tmp < keylen) {
     103           0 :                 goto overflow;
     104             :         }
     105             : 
     106     1420447 :         len += tmp;
     107     1420447 :         if (len < tmp) {
     108           0 :                 goto overflow;
     109             :         }
     110             : 
     111     1420447 :         len += valuelen;
     112     1420447 :         if (len < valuelen) {
     113           0 :                 goto overflow;
     114             :         }
     115             : 
     116     1420447 :         return len;
     117           0 : overflow:
     118           0 :         return -1;
     119             : }
     120             : 
     121     1420452 : static NTSTATUS db_rbt_storev(struct db_record *rec,
     122             :                               const TDB_DATA *dbufs, int num_dbufs, int flag)
     123             : {
     124     1420452 :         struct db_rbt_ctx *db_ctx = talloc_get_type_abort(
     125             :                 rec->db->private_data, struct db_rbt_ctx);
     126     1420452 :         struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
     127             :         struct db_rbt_node *node;
     128             : 
     129             :         struct rb_node ** p;
     130     1420452 :         struct rb_node *parent = NULL;
     131     1420452 :         struct db_rbt_node *parent_node = NULL;
     132             : 
     133             :         ssize_t reclen;
     134             :         TDB_DATA data, this_key, this_val;
     135     1420452 :         void *to_free = NULL;
     136             : 
     137     1420452 :         if (db_ctx->traverse_read > 0) {
     138           0 :                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
     139             :         }
     140             : 
     141     1420452 :         if ((flag == TDB_INSERT) && (rec_priv->node != NULL)) {
     142           0 :                 return NT_STATUS_OBJECT_NAME_COLLISION;
     143             :         }
     144             : 
     145     1420452 :         if ((flag == TDB_MODIFY) && (rec_priv->node == NULL)) {
     146           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     147             :         }
     148             : 
     149     1420452 :         if (num_dbufs == 1) {
     150     1420452 :                 data = dbufs[0];
     151             :         } else {
     152           0 :                 data = dbwrap_merge_dbufs(rec, dbufs, num_dbufs);
     153           0 :                 if (data.dptr == NULL) {
     154           0 :                         return NT_STATUS_NO_MEMORY;
     155             :                 }
     156           0 :                 to_free = data.dptr;
     157             :         }
     158             : 
     159     1420452 :         if (rec_priv->node != NULL) {
     160             : 
     161             :                 /*
     162             :                  * The record was around previously
     163             :                  */
     164             : 
     165           5 :                 db_rbt_parse_node(rec_priv->node, &this_key, &this_val);
     166             : 
     167           5 :                 SMB_ASSERT(this_key.dsize == rec->key.dsize);
     168           5 :                 SMB_ASSERT(memcmp(this_key.dptr, rec->key.dptr,
     169             :                                   this_key.dsize) == 0);
     170             : 
     171           5 :                 if (this_val.dsize >= data.dsize) {
     172             :                         /*
     173             :                          * The new value fits into the old space
     174             :                          */
     175           5 :                         memcpy(this_val.dptr, data.dptr, data.dsize);
     176           5 :                         rec_priv->node->valuesize = data.dsize;
     177           5 :                         TALLOC_FREE(to_free);
     178           5 :                         return NT_STATUS_OK;
     179             :                 }
     180             :         }
     181             : 
     182     1420447 :         reclen = db_rbt_reclen(rec->key.dsize, data.dsize);
     183     1420447 :         if (reclen == -1) {
     184           0 :                 TALLOC_FREE(to_free);
     185           0 :                 return NT_STATUS_INSUFFICIENT_RESOURCES;
     186             :         }
     187             : 
     188     1420447 :         node = talloc_zero_size(db_ctx, reclen);
     189     1420447 :         if (node == NULL) {
     190           0 :                 TALLOC_FREE(to_free);
     191           0 :                 return NT_STATUS_NO_MEMORY;
     192             :         }
     193             : 
     194     1420447 :         if (rec_priv->node != NULL) {
     195           0 :                 if (db_ctx->traverse_nextp != NULL) {
     196           0 :                         if (*db_ctx->traverse_nextp == rec_priv->node) {
     197           0 :                                 *db_ctx->traverse_nextp = node;
     198             :                         }
     199             :                 }
     200             : 
     201             :                 /*
     202             :                  * We need to delete the key from the tree and start fresh,
     203             :                  * there's not enough space in the existing record
     204             :                  */
     205             : 
     206           0 :                 rb_erase(&rec_priv->node->rb_node, &db_ctx->tree);
     207           0 :                 DLIST_REMOVE(db_ctx->nodes, rec_priv->node);
     208             : 
     209             :                 /*
     210             :                  * Keep the existing node around for a while: If the record
     211             :                  * existed before, we reference the key data in there.
     212             :                  */
     213             :         }
     214             : 
     215     1420447 :         node->keysize = rec->key.dsize;
     216     1420447 :         node->valuesize = data.dsize;
     217             : 
     218     1420447 :         db_rbt_parse_node(node, &this_key, &this_val);
     219             : 
     220     1420447 :         memcpy(this_key.dptr, rec->key.dptr, node->keysize);
     221     1420447 :         TALLOC_FREE(rec_priv->node);
     222     1420447 :         rec_priv->node = node;
     223             : 
     224     1420447 :         if (node->valuesize > 0) {
     225      387295 :                 memcpy(this_val.dptr, data.dptr, node->valuesize);
     226             :         }
     227             : 
     228     1420447 :         parent = NULL;
     229     1420447 :         p = &db_ctx->tree.rb_node;
     230             : 
     231    15594639 :         while (*p) {
     232             :                 struct db_rbt_node *r;
     233             :                 TDB_DATA search_key, search_val;
     234             :                 int res;
     235             : 
     236    12918390 :                 r = db_rbt2node(*p);
     237             : 
     238    12918390 :                 parent = (*p);
     239    12918390 :                 parent_node = r;
     240             : 
     241    12918390 :                 db_rbt_parse_node(r, &search_key, &search_val);
     242             : 
     243    12918390 :                 res = db_rbt_compare(this_key, search_key);
     244             : 
     245    12918390 :                 if (res == -1) {
     246     6381991 :                         p = &(*p)->rb_left;
     247             :                 }
     248     6536399 :                 else if (res == 1) {
     249     6536399 :                         p = &(*p)->rb_right;
     250             :                 }
     251             :                 else {
     252           0 :                         smb_panic("someone messed with the tree");
     253             :                 }
     254             :         }
     255             : 
     256     1420447 :         rb_link_node(&node->rb_node, parent, p);
     257     1420447 :         DLIST_ADD_AFTER(db_ctx->nodes, node, parent_node);
     258     1420447 :         rb_insert_color(&node->rb_node, &db_ctx->tree);
     259             : 
     260     1420447 :         TALLOC_FREE(to_free);
     261             : 
     262     1420447 :         return NT_STATUS_OK;
     263             : }
     264             : 
     265       65314 : static NTSTATUS db_rbt_delete(struct db_record *rec)
     266             : {
     267       65314 :         struct db_rbt_ctx *db_ctx = talloc_get_type_abort(
     268             :                 rec->db->private_data, struct db_rbt_ctx);
     269       65314 :         struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
     270             : 
     271       65314 :         if (db_ctx->traverse_read > 0) {
     272           0 :                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
     273             :         }
     274             : 
     275       65314 :         if (rec_priv->node == NULL) {
     276        2078 :                 return NT_STATUS_OK;
     277             :         }
     278             : 
     279       63236 :         if (db_ctx->traverse_nextp != NULL) {
     280        7983 :                 if (*db_ctx->traverse_nextp == rec_priv->node) {
     281           0 :                         *db_ctx->traverse_nextp = rec_priv->node->next;
     282             :                 }
     283             :         }
     284             : 
     285       63236 :         rb_erase(&rec_priv->node->rb_node, &db_ctx->tree);
     286       63236 :         DLIST_REMOVE(db_ctx->nodes, rec_priv->node);
     287       63236 :         TALLOC_FREE(rec_priv->node);
     288             : 
     289       63236 :         return NT_STATUS_OK;
     290             : }
     291             : 
     292             : struct db_rbt_search_result {
     293             :         TDB_DATA key;
     294             :         TDB_DATA val;
     295             :         struct db_rbt_node* node;
     296             : };
     297             : 
     298    12279024 : static bool db_rbt_search_internal(struct db_context *db, TDB_DATA key,
     299             :                                    struct db_rbt_search_result *result)
     300             : {
     301    12279024 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     302             :                 db->private_data, struct db_rbt_ctx);
     303             : 
     304             :         struct rb_node *n;
     305    12279024 :         bool found = false;
     306    12279024 :         struct db_rbt_node *r = NULL;
     307    12279024 :         TDB_DATA search_key = { 0 };
     308    12279024 :         TDB_DATA search_val = { 0 };
     309             : 
     310    12279024 :         n = ctx->tree.rb_node;
     311             : 
     312    59937696 :         while (n != NULL) {
     313             :                 int res;
     314             : 
     315    45091432 :                 r = db_rbt2node(n);
     316             : 
     317    45091432 :                 db_rbt_parse_node(r, &search_key, &search_val);
     318             : 
     319    45091432 :                 res = db_rbt_compare(key, search_key);
     320             : 
     321    45091432 :                 if (res == -1) {
     322    17869921 :                         n = n->rb_left;
     323             :                 }
     324    27221511 :                 else if (res == 1) {
     325    18207647 :                         n = n->rb_right;
     326             :                 }
     327             :                 else {
     328     9013864 :                         found = true;
     329     9013864 :                         break;
     330             :                 }
     331             :         }
     332    12279024 :         if (result != NULL) {
     333    11522919 :                 if (found) {
     334     8639338 :                         result->key = search_key;
     335     8639338 :                         result->val = search_val;
     336     8639338 :                         result->node = r;
     337             :                 } else {
     338     2883581 :                         ZERO_STRUCT(*result);
     339             :                 }
     340             :         }
     341    12279024 :         return found;
     342             : }
     343             : 
     344     8572487 : static struct db_record *db_rbt_fetch_locked(struct db_context *db_ctx,
     345             :                                              TALLOC_CTX *mem_ctx,
     346             :                                              TDB_DATA key)
     347             : {
     348             :         struct db_rbt_rec *rec_priv;
     349             :         struct db_record *result;
     350             :         size_t size;
     351             :         bool found;
     352             :         struct db_rbt_search_result res;
     353             : 
     354     8572487 :         found = db_rbt_search_internal(db_ctx, key, &res);
     355             : 
     356             :         /*
     357             :          * In this low-level routine, play tricks to reduce the number of
     358             :          * tallocs to one. Not recommened for general use, but here it pays
     359             :          * off.
     360             :          */
     361             : 
     362     8572487 :         size = DBWRAP_RBT_ALIGN(sizeof(struct db_record))
     363             :                 + sizeof(struct db_rbt_rec);
     364             : 
     365     8572487 :         if (!found) {
     366             :                 /*
     367             :                  * We need to keep the key around for later store
     368             :                  */
     369     1422525 :                 size += key.dsize;
     370             :         }
     371             : 
     372     8572487 :         result = (struct db_record *)talloc_size(mem_ctx, size);
     373     8572487 :         if (result == NULL) {
     374           0 :                 return NULL;
     375             :         }
     376             : 
     377     8572487 :         rec_priv = (struct db_rbt_rec *)
     378             :                 ((char *)result + DBWRAP_RBT_ALIGN(sizeof(struct db_record)));
     379             : 
     380     8572487 :         result->storev = db_rbt_storev;
     381     8572487 :         result->delete_rec = db_rbt_delete;
     382     8572487 :         result->private_data = rec_priv;
     383             : 
     384     8572487 :         rec_priv->node = res.node;
     385     8572487 :         result->value  = res.val;
     386     8572487 :         result->value_valid = true;
     387             : 
     388     8572487 :         if (found) {
     389     7149962 :                 result->key = res.key;
     390             :         }
     391             :         else {
     392     1422525 :                 result->key.dptr = (uint8_t *)
     393             :                         ((char *)rec_priv + sizeof(*rec_priv));
     394     1422525 :                 result->key.dsize = key.dsize;
     395     1422525 :                 memcpy(result->key.dptr, key.dptr, key.dsize);
     396             :         }
     397             : 
     398     8572487 :         return result;
     399             : }
     400             : 
     401      756105 : static int db_rbt_exists(struct db_context *db, TDB_DATA key)
     402             : {
     403      756105 :         return db_rbt_search_internal(db, key, NULL);
     404             : }
     405             : 
     406           0 : static int db_rbt_wipe(struct db_context *db)
     407             : {
     408           0 :         struct db_rbt_ctx *old_ctx = talloc_get_type_abort(
     409             :                 db->private_data, struct db_rbt_ctx);
     410           0 :         struct db_rbt_ctx *new_ctx = talloc_zero(db, struct db_rbt_ctx);
     411           0 :         if (new_ctx == NULL) {
     412           0 :                 return -1;
     413             :         }
     414           0 :         db->private_data = new_ctx;
     415           0 :         talloc_free(old_ctx);
     416           0 :         return 0;
     417             : }
     418             : 
     419     2950432 : static NTSTATUS db_rbt_parse_record(struct db_context *db, TDB_DATA key,
     420             :                                     void (*parser)(TDB_DATA key, TDB_DATA data,
     421             :                                                    void *private_data),
     422             :                                     void *private_data)
     423             : {
     424             :         struct db_rbt_search_result res;
     425     2950432 :         bool found = db_rbt_search_internal(db, key, &res);
     426             : 
     427     2950432 :         if (!found) {
     428     1461056 :                 return NT_STATUS_NOT_FOUND;
     429             :         }
     430     1489376 :         parser(res.key, res.val, private_data);
     431     1489376 :         return NT_STATUS_OK;
     432             : }
     433             : 
     434       15852 : static int db_rbt_traverse_internal(struct db_context *db,
     435             :                                     int (*f)(struct db_record *db,
     436             :                                              void *private_data),
     437             :                                     void *private_data, uint32_t* count,
     438             :                                     bool rw)
     439             : {
     440       15852 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     441             :                 db->private_data, struct db_rbt_ctx);
     442       15852 :         struct db_rbt_node *cur = NULL;
     443       15852 :         struct db_rbt_node *next = NULL;
     444             :         int ret;
     445             : 
     446       23919 :         for (cur = ctx->nodes; cur != NULL; cur = next) {
     447             :                 struct db_record rec;
     448             :                 struct db_rbt_rec rec_priv;
     449             : 
     450        8067 :                 rec_priv.node = cur;
     451        8067 :                 next = rec_priv.node->next;
     452             : 
     453        8067 :                 ZERO_STRUCT(rec);
     454        8067 :                 rec.db = db;
     455        8067 :                 rec.private_data = &rec_priv;
     456        8067 :                 rec.storev = db_rbt_storev;
     457        8067 :                 rec.delete_rec = db_rbt_delete;
     458        8067 :                 db_rbt_parse_node(rec_priv.node, &rec.key, &rec.value);
     459        8067 :                 rec.value_valid = true;
     460             : 
     461        8067 :                 if (rw) {
     462        8067 :                         ctx->traverse_nextp = &next;
     463             :                 }
     464        8067 :                 ret = f(&rec, private_data);
     465        8067 :                 (*count) ++;
     466        8067 :                 if (rw) {
     467        8067 :                         ctx->traverse_nextp = NULL;
     468             :                 }
     469        8067 :                 if (ret != 0) {
     470           0 :                         return ret;
     471             :                 }
     472        8067 :                 if (rec_priv.node != NULL) {
     473          84 :                         next = rec_priv.node->next;
     474             :                 }
     475             :         }
     476             : 
     477       15852 :         return 0;
     478             : }
     479             : 
     480           0 : static int db_rbt_traverse_read(struct db_context *db,
     481             :                                 int (*f)(struct db_record *db,
     482             :                                          void *private_data),
     483             :                                 void *private_data)
     484             : {
     485           0 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     486             :                 db->private_data, struct db_rbt_ctx);
     487           0 :         uint32_t count = 0;
     488             :         int ret;
     489             : 
     490           0 :         ctx->traverse_read++;
     491           0 :         ret = db_rbt_traverse_internal(db,
     492             :                                        f, private_data, &count,
     493             :                                        false /* rw */);
     494           0 :         ctx->traverse_read--;
     495           0 :         if (ret != 0) {
     496           0 :                 return -1;
     497             :         }
     498           0 :         if (count > INT_MAX) {
     499           0 :                 return -1;
     500             :         }
     501           0 :         return count;
     502             : }
     503             : 
     504       15852 : static int db_rbt_traverse(struct db_context *db,
     505             :                            int (*f)(struct db_record *db,
     506             :                                     void *private_data),
     507             :                            void *private_data)
     508             : {
     509       15852 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     510             :                 db->private_data, struct db_rbt_ctx);
     511       15852 :         uint32_t count = 0;
     512             :         int ret;
     513             : 
     514       15852 :         if (ctx->traverse_nextp != NULL) {
     515           0 :                 return -1;
     516             :         };
     517             : 
     518       15852 :         if (ctx->traverse_read > 0) {
     519           0 :                 return db_rbt_traverse_read(db, f, private_data);
     520             :         }
     521             : 
     522       15852 :         ret = db_rbt_traverse_internal(db,
     523             :                                        f, private_data, &count,
     524             :                                        true /* rw */);
     525       15852 :         if (ret != 0) {
     526           0 :                 return -1;
     527             :         }
     528       15852 :         if (count > INT_MAX) {
     529           0 :                 return -1;
     530             :         }
     531       15852 :         return count;
     532             : }
     533             : 
     534           0 : static int db_rbt_get_seqnum(struct db_context *db)
     535             : {
     536           0 :         return 0;
     537             : }
     538             : 
     539           0 : static int db_rbt_trans_dummy(struct db_context *db)
     540             : {
     541             :         /*
     542             :          * Transactions are pretty pointless in-memory, just return success.
     543             :          */
     544           0 :         return 0;
     545             : }
     546             : 
     547           0 : static size_t db_rbt_id(struct db_context *db, uint8_t *id, size_t idlen)
     548             : {
     549           0 :         if (idlen >= sizeof(struct db_context *)) {
     550           0 :                 memcpy(id, &db, sizeof(struct db_context *));
     551             :         }
     552           0 :         return sizeof(struct db_context *);
     553             : }
     554             : 
     555      742775 : struct db_context *db_open_rbt(TALLOC_CTX *mem_ctx)
     556             : {
     557             :         struct db_context *result;
     558             : 
     559      742775 :         result = talloc_zero(mem_ctx, struct db_context);
     560             : 
     561      742775 :         if (result == NULL) {
     562           0 :                 return NULL;
     563             :         }
     564             : 
     565      742775 :         result->private_data = talloc_zero(result, struct db_rbt_ctx);
     566             : 
     567      742775 :         if (result->private_data == NULL) {
     568           0 :                 TALLOC_FREE(result);
     569           0 :                 return NULL;
     570             :         }
     571             : 
     572      742775 :         result->fetch_locked = db_rbt_fetch_locked;
     573      742775 :         result->traverse = db_rbt_traverse;
     574      742775 :         result->traverse_read = db_rbt_traverse_read;
     575      742775 :         result->get_seqnum = db_rbt_get_seqnum;
     576      742775 :         result->transaction_start = db_rbt_trans_dummy;
     577      742775 :         result->transaction_commit = db_rbt_trans_dummy;
     578      742775 :         result->transaction_cancel = db_rbt_trans_dummy;
     579      742775 :         result->exists = db_rbt_exists;
     580      742775 :         result->wipe = db_rbt_wipe;
     581      742775 :         result->parse_record = db_rbt_parse_record;
     582      742775 :         result->id = db_rbt_id;
     583      742775 :         result->name = "dbwrap rbt";
     584             : 
     585      742775 :         return result;
     586             : }

Generated by: LCOV version 1.13