From e493e4365431ed3672fc914b907dded442bc82ad Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@openldap.org>
Date: Fri, 16 Apr 2010 00:22:28 +0000
Subject: [PATCH] use slab memory as much as possible

---
 servers/slapd/back-meta/back-meta.h |   9 +-
 servers/slapd/back-meta/map.c       | 124 ++++++++++++++++------------
 servers/slapd/back-meta/search.c    |  38 +++++----
 3 files changed, 100 insertions(+), 71 deletions(-)

diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h
index 4fd75cb4b7..ee14f97dcd 100644
--- a/servers/slapd/back-meta/back-meta.h
+++ b/servers/slapd/back-meta/back-meta.h
@@ -117,7 +117,8 @@ ldap_back_map_attrs(
 	struct ldapmap *at_map,
 	AttributeName *a,
 	int remap,
-	char ***mapped_attrs );
+	char ***mapped_attrs,
+	void *memctx );
 
 extern int ldap_back_map_config(
 	struct ldapmap	*oc_map,
@@ -132,7 +133,8 @@ ldap_back_filter_map_rewrite(
 	dncookie	*dc,
 	Filter		*f,
 	struct berval	*fstr,
-	int		remap );
+	int		remap,
+	void		*memctx );
 
 /* suffix massaging by means of librewrite */
 #ifdef ENABLE_REWRITE
@@ -146,7 +148,8 @@ suffix_massage_config( struct rewrite_info *info,
 extern int
 ldap_back_referral_result_rewrite(
 	dncookie	*dc,
-	BerVarray	a_vals );
+	BerVarray	a_vals,
+	void		*memctx );
 extern int
 ldap_dnattr_rewrite(
 	dncookie	*dc,
diff --git a/servers/slapd/back-meta/map.c b/servers/slapd/back-meta/map.c
index 1b92b009e2..7df929eb41 100644
--- a/servers/slapd/back-meta/map.c
+++ b/servers/slapd/back-meta/map.c
@@ -177,8 +177,8 @@ ldap_back_map_attrs(
 		struct ldapmap *at_map,
 		AttributeName *an,
 		int remap,
-		char ***mapped_attrs
-)
+		char ***mapped_attrs,
+		void *memctx )
 {
 	int i, j;
 	char **na;
@@ -192,7 +192,7 @@ ldap_back_map_attrs(
 	for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
 		/*  */ ;
 
-	na = (char **)ch_calloc( i + 1, sizeof(char *) );
+	na = (char **)ber_memcalloc_x( i + 1, sizeof(char *), memctx );
 	if ( na == NULL ) {
 		*mapped_attrs = NULL;
 		return LDAP_NO_MEMORY;
@@ -213,14 +213,15 @@ ldap_back_map_attrs(
 	return LDAP_SUCCESS;
 }
 
-int
+static int
 map_attr_value(
 		dncookie		*dc,
 		AttributeDescription 	*ad,
 		struct berval		*mapped_attr,
 		struct berval		*value,
 		struct berval		*mapped_value,
-		int			remap )
+		int			remap,
+		void			*memctx )
 {
 	struct berval		vtmp;
 	int			freeval = 0;
@@ -272,11 +273,11 @@ map_attr_value(
 	} else if ( ad->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER ) {
 		if ( ad->ad_type->sat_equality->smr_normalize(
 			(SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
-			NULL, NULL, value, &vtmp, NULL ) )
+			NULL, NULL, value, &vtmp, memctx ) )
 		{
 			return -1;
 		}
-		freeval = 1;
+		freeval = 2;
 
 	} else if ( ad == slap_schema.si_ad_objectClass || ad == slap_schema.si_ad_structuralObjectClass ) {
 		ldap_back_map( &dc->target->mt_rwmap.rwm_oc, value, &vtmp, remap );
@@ -288,10 +289,15 @@ map_attr_value(
 		vtmp = *value;
 	}
 
-	filter_escape_value( &vtmp, mapped_value );
+	filter_escape_value_x( &vtmp, mapped_value, memctx );
 
-	if ( freeval ) {
+	switch ( freeval ) {
+	case 1:
 		ber_memfree( vtmp.bv_val );
+		break;
+	case 2:
+		ber_memfree_x( vtmp.bv_val, memctx );
+		break;
 	}
 	
 	return 0;
@@ -301,8 +307,9 @@ static int
 ldap_back_int_filter_map_rewrite(
 		dncookie		*dc,
 		Filter			*f,
-		struct berval		*fstr,
-		int			remap )
+		struct berval	*fstr,
+		int				remap,
+		void			*memctx )
 {
 	int		i;
 	Filter		*p;
@@ -329,82 +336,82 @@ ldap_back_int_filter_map_rewrite(
 	BER_BVZERO( fstr );
 
 	if ( f == NULL ) {
-		ber_dupbv( fstr, &ber_bvnone );
+		ber_dupbv_x( fstr, &ber_bvnone, memctx );
 		return LDAP_OTHER;
 	}
 
 	switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
 	case LDAP_FILTER_EQUALITY:
 		if ( map_attr_value( dc, f->f_av_desc, &atmp,
-					&f->f_av_value, &vtmp, remap ) )
+					&f->f_av_value, &vtmp, remap, memctx ) )
 		{
 			goto computed;
 		}
 
 		fstr->bv_len = atmp.bv_len + vtmp.bv_len
 			+ ( sizeof("(=)") - 1 );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
 
-		ber_memfree( vtmp.bv_val );
+		ber_memfree_x( vtmp.bv_val, memctx );
 		break;
 
 	case LDAP_FILTER_GE:
 		if ( map_attr_value( dc, f->f_av_desc, &atmp,
-					&f->f_av_value, &vtmp, remap ) )
+					&f->f_av_value, &vtmp, remap, memctx ) )
 		{
 			goto computed;
 		}
 
 		fstr->bv_len = atmp.bv_len + vtmp.bv_len
 			+ ( sizeof("(>=)") - 1 );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
 
-		ber_memfree( vtmp.bv_val );
+		ber_memfree_x( vtmp.bv_val, memctx );
 		break;
 
 	case LDAP_FILTER_LE:
 		if ( map_attr_value( dc, f->f_av_desc, &atmp,
-					&f->f_av_value, &vtmp, remap ) )
+					&f->f_av_value, &vtmp, remap, memctx ) )
 		{
 			goto computed;
 		}
 
 		fstr->bv_len = atmp.bv_len + vtmp.bv_len
 			+ ( sizeof("(<=)") - 1 );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
 
-		ber_memfree( vtmp.bv_val );
+		ber_memfree_x( vtmp.bv_val, memctx );
 		break;
 
 	case LDAP_FILTER_APPROX:
 		if ( map_attr_value( dc, f->f_av_desc, &atmp,
-					&f->f_av_value, &vtmp, remap ) )
+					&f->f_av_value, &vtmp, remap, memctx ) )
 		{
 			goto computed;
 		}
 
 		fstr->bv_len = atmp.bv_len + vtmp.bv_len
 			+ ( sizeof("(~=)") - 1 );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
 
-		ber_memfree( vtmp.bv_val );
+		ber_memfree_x( vtmp.bv_val, memctx );
 		break;
 
 	case LDAP_FILTER_SUBSTRINGS:
 		if ( map_attr_value( dc, f->f_sub_desc, &atmp,
-					NULL, NULL, remap ) )
+					NULL, NULL, remap, memctx ) )
 		{
 			goto computed;
 		}
@@ -412,7 +419,7 @@ ldap_back_int_filter_map_rewrite(
 		/* cannot be a DN ... */
 
 		fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 128 ); /* FIXME: why 128 ? */
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
 			atmp.bv_val );
@@ -420,59 +427,59 @@ ldap_back_int_filter_map_rewrite(
 		if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
 			len = fstr->bv_len;
 
-			filter_escape_value( &f->f_sub_initial, &vtmp );
+			filter_escape_value_x( &f->f_sub_initial, &vtmp, memctx );
 
 			fstr->bv_len += vtmp.bv_len;
-			fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+			fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
 
 			snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
 				/* "(attr=" */ "%s*)",
 				vtmp.bv_len ? vtmp.bv_val : "" );
 
-			ber_memfree( vtmp.bv_val );
+			ber_memfree_x( vtmp.bv_val, memctx );
 		}
 
 		if ( f->f_sub_any != NULL ) {
 			for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
 				len = fstr->bv_len;
-				filter_escape_value( &f->f_sub_any[i], &vtmp );
+				filter_escape_value_x( &f->f_sub_any[i], &vtmp, memctx );
 
 				fstr->bv_len += vtmp.bv_len + 1;
-				fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+				fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
 
 				snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
 					/* "(attr=[init]*[any*]" */ "%s*)",
 					vtmp.bv_len ? vtmp.bv_val : "" );
-				ber_memfree( vtmp.bv_val );
+				ber_memfree_x( vtmp.bv_val, memctx );
 			}
 		}
 
 		if ( !BER_BVISNULL( &f->f_sub_final ) ) {
 			len = fstr->bv_len;
 
-			filter_escape_value( &f->f_sub_final, &vtmp );
+			filter_escape_value_x( &f->f_sub_final, &vtmp, memctx );
 
 			fstr->bv_len += vtmp.bv_len;
-			fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+			fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
 
 			snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
 				/* "(attr=[init*][any*]" */ "%s)",
 				vtmp.bv_len ? vtmp.bv_val : "" );
 
-			ber_memfree( vtmp.bv_val );
+			ber_memfree_x( vtmp.bv_val, memctx );
 		}
 
 		break;
 
 	case LDAP_FILTER_PRESENT:
 		if ( map_attr_value( dc, f->f_desc, &atmp,
-					NULL, NULL, remap ) )
+					NULL, NULL, remap, memctx ) )
 		{
 			goto computed;
 		}
 
 		fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
 			atmp.bv_val );
@@ -482,7 +489,7 @@ ldap_back_int_filter_map_rewrite(
 	case LDAP_FILTER_OR:
 	case LDAP_FILTER_NOT:
 		fstr->bv_len = STRLENOF( "(%)" );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 128 );	/* FIXME: why 128? */
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx );	/* FIXME: why 128? */
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
 			f->f_choice == LDAP_FILTER_AND ? '&' :
@@ -493,18 +500,18 @@ ldap_back_int_filter_map_rewrite(
 
 			len = fstr->bv_len;
 
-			rc = ldap_back_int_filter_map_rewrite( dc, p, &vtmp, remap );
+			rc = ldap_back_int_filter_map_rewrite( dc, p, &vtmp, remap, memctx );
 			if ( rc != LDAP_SUCCESS ) {
 				return rc;
 			}
 			
 			fstr->bv_len += vtmp.bv_len;
-			fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+			fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
 
 			snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2, 
 				/*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
 
-			ch_free( vtmp.bv_val );
+			ber_memfree_x( vtmp.bv_val, memctx );
 		}
 
 		break;
@@ -512,14 +519,14 @@ ldap_back_int_filter_map_rewrite(
 	case LDAP_FILTER_EXT:
 		if ( f->f_mr_desc ) {
 			if ( map_attr_value( dc, f->f_mr_desc, &atmp,
-						&f->f_mr_value, &vtmp, remap ) )
+						&f->f_mr_value, &vtmp, remap, memctx ) )
 			{
 				goto computed;
 			}
 
 		} else {
 			BER_BVSTR( &atmp, "" );
-			filter_escape_value( &f->f_mr_value, &vtmp );
+			filter_escape_value_x( &f->f_mr_value, &vtmp, memctx );
 		}
 
 		/* FIXME: cleanup (less ?: operators...) */
@@ -527,7 +534,7 @@ ldap_back_int_filter_map_rewrite(
 			( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
 			( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
 			vtmp.bv_len + ( STRLENOF( "(:=)" ) );
-		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+		fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
 
 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
 			atmp.bv_val,
@@ -535,7 +542,7 @@ ldap_back_int_filter_map_rewrite(
 			!BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
 			!BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
 			vtmp.bv_len ? vtmp.bv_val : "" );
-		ber_memfree( vtmp.bv_val );
+		ber_memfree_x( vtmp.bv_val, memctx );
 		break;
 
 	case SLAPD_FILTER_COMPUTED:
@@ -570,11 +577,11 @@ computed:;
 			break;
 		}
 
-		ber_dupbv( fstr, tmp );
+		ber_dupbv_x( fstr, tmp, memctx );
 		break;
 
 	default:
-		ber_dupbv( fstr, &ber_bvunknown );
+		ber_dupbv_x( fstr, &ber_bvunknown, memctx );
 		break;
 	}
 
@@ -585,15 +592,16 @@ int
 ldap_back_filter_map_rewrite(
 		dncookie		*dc,
 		Filter			*f,
-		struct berval		*fstr,
-		int			remap )
+		struct berval	*fstr,
+		int				remap,
+		void			*memctx )
 {
 	int		rc;
 	dncookie	fdc;
 	struct berval	ftmp;
 	static char	*dmy = "";
 
-	rc = ldap_back_int_filter_map_rewrite( dc, f, fstr, remap );
+	rc = ldap_back_int_filter_map_rewrite( dc, f, fstr, remap, memctx );
 
 #ifdef ENABLE_REWRITE
 	if ( rc != LDAP_SUCCESS ) {
@@ -642,6 +650,15 @@ ldap_back_filter_map_rewrite(
 
 	if ( fstr->bv_val == dmy ) {
 		BER_BVZERO( fstr );
+
+	} else if ( fstr->bv_val != ftmp.bv_val ) {
+		/* NOTE: need to realloc mapped filter on slab
+		 * and free the original one, until librewrite
+		 * becomes slab-aware
+		 */
+		ber_dupbv_x( &ftmp, fstr, memctx );
+		ch_free( fstr->bv_val );
+		*fstr = ftmp;
 	}
 #endif /* ENABLE_REWRITE */
 
@@ -651,7 +668,8 @@ ldap_back_filter_map_rewrite(
 int
 ldap_back_referral_result_rewrite(
 	dncookie		*dc,
-	BerVarray		a_vals
+	BerVarray		a_vals,
+	void			*memctx
 )
 {
 	int		i, last;
@@ -718,8 +736,8 @@ ldap_back_referral_result_rewrite(
 					break;
 				}
 
-				LBER_FREE( a_vals[ i ].bv_val );
-				ber_str2bv( newurl, 0, 1, &a_vals[ i ] );
+				ber_memfree_x( a_vals[ i ].bv_val, memctx );
+				ber_str2bv_x( newurl, 0, 1, &a_vals[ i ], memctx );
 				LDAP_FREE( newurl );
 				ludp->lud_dn = olddn.bv_val;
 			}
diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c
index 3e2930dd07..ae1a4f64e4 100644
--- a/servers/slapd/back-meta/search.c
+++ b/servers/slapd/back-meta/search.c
@@ -556,7 +556,7 @@ meta_back_search_start(
 	 * Maps filter
 	 */
 	rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
-			&mfilter, BACKLDAP_MAP );
+			&mfilter, BACKLDAP_MAP, op->o_tmpmemctx );
 	switch ( rc ) {
 	case LDAP_SUCCESS:
 		break;
@@ -574,7 +574,8 @@ meta_back_search_start(
 	 * Maps required attributes
 	 */
 	rc = ldap_back_map_attrs( &mt->mt_rwmap.rwm_at,
-			op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
+			op->ors_attrs, BACKLDAP_MAP, &mapped_attrs,
+			op->o_tmpmemctx );
 	if ( rc != LDAP_SUCCESS ) {
 		/*
 		 * this target is no longer candidate
@@ -637,10 +638,10 @@ done:;
 	(void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
 
 	if ( mapped_attrs ) {
-		free( mapped_attrs );
+		ber_memfree_x( mapped_attrs, op->o_tmpmemctx );
 	}
 	if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
-		free( mfilter.bv_val );
+		ber_memfree_x( mfilter.bv_val, op->o_tmpmemctx );
 	}
 	if ( mbase.bv_val != realbase.bv_val ) {
 		free( mbase.bv_val );
@@ -1182,14 +1183,17 @@ really_bad:;
 					for ( cnt = 0; references[ cnt ]; cnt++ )
 						;
 	
-					rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
+					rs->sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ),
+						op->o_tmpmemctx );
 	
 					for ( cnt = 0; references[ cnt ]; cnt++ ) {
-						ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
+						ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
+						op->o_tmpmemctx );
 					}
 					BER_BVZERO( &rs->sr_ref[ cnt ] );
 	
-					( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
+					( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref,
+						op->o_tmpmemctx );
 
 					if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
 						/* ignore return value by now */
@@ -1198,7 +1202,7 @@ really_bad:;
 						( void )send_search_reference( op, rs );
 						op->o_private = savepriv;
 	
-						ber_bvarray_free( rs->sr_ref );
+						ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
 						rs->sr_ref = NULL;
 					}
 
@@ -1333,23 +1337,27 @@ really_bad:;
 							for ( cnt = 0; references[ cnt ]; cnt++ )
 								;
 	
-							sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
+							sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ),
+								op->o_tmpmemctx );
 	
 							for ( cnt = 0; references[ cnt ]; cnt++ ) {
-								ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
+								ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
+									op->o_tmpmemctx );
 							}
 							BER_BVZERO( &sr_ref[ cnt ] );
 	
-							( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
+							( void )ldap_back_referral_result_rewrite( &dc, sr_ref,
+								op->o_tmpmemctx );
 					
 							if ( rs->sr_v2ref == NULL ) {
 								rs->sr_v2ref = sr_ref;
 
 							} else {
 								for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
-									ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
+									ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
+										op->o_tmpmemctx );
 								}
-								ber_memfree( sr_ref );
+								ber_memfree_x( sr_ref, op->o_tmpmemctx );
 							}
 						}
 
@@ -1706,7 +1714,7 @@ finish:;
 	}
 
 	if ( rs->sr_v2ref ) {
-		ber_bvarray_free( rs->sr_v2ref );
+		ber_bvarray_free_x( rs->sr_v2ref, op->o_tmpmemctx );
 	}
 
 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
@@ -2036,7 +2044,7 @@ remove_oc:;
 				ldap_dnattr_result_rewrite( &dc, attr->a_vals );
 
 			} else if ( attr->a_desc == slap_schema.si_ad_ref ) {
-				ldap_back_referral_result_rewrite( &dc, attr->a_vals );
+				ldap_back_referral_result_rewrite( &dc, attr->a_vals, NULL );
 
 			}
 
-- 
GitLab