]> git.sur5r.net Git - openldap/blob - libraries/liblber/memory.c
f45ec52b68d8d6968d6e2a4d94b90d795f8cb134
[openldap] / libraries / liblber / memory.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2006 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 #include "portable.h"
17
18 #include <ac/stdlib.h>
19 #include <ac/string.h>
20
21 #include "lber-int.h"
22
23 #ifdef LDAP_MEMORY_TRACE
24 #include <stdio.h>
25 #endif
26
27 #if LDAP_MEMORY_DEBUG
28 /*
29  * LDAP_MEMORY_DEBUG should only be enabled for the purposes of
30  * debugging memory management within OpenLDAP libraries and slapd.
31  * It should only be enabled by an experienced developer as it
32  * causes the inclusion of numerous assert()'s, many of which may
33  * be triggered by a prefectly valid program.
34  *
35  * The code behind this macro is subject to change as needed to
36  * support this testing.
37  */
38
39 struct ber_mem_hdr {
40         ber_int_t       bm_top; /* Pattern to detect buf overrun from prev buffer */
41         ber_int_t       bm_length; /* Length of user allocated area */
42 #ifdef LDAP_MEMORY_TRACE
43         ber_int_t       bm_sequence; /* Allocation sequence number */
44 #endif
45         union bmu_align_u {     /* Force alignment, pattern to detect back clobber */
46                 ber_len_t       bmu_len_t;
47                 ber_tag_t       bmu_tag_t;
48                 ber_int_t       bmu_int_t;
49
50                 size_t  bmu_size_t;
51                 void *  bmu_voidp;
52                 double  bmu_double;
53                 long    bmu_long;
54                 long    (*bmu_funcp)( double );
55                 unsigned char   bmu_char[4];
56         } ber_align;
57 #define bm_junk ber_align.bmu_len_t
58 #define bm_data ber_align.bmu_char[1]
59 #define bm_char ber_align.bmu_char
60 };
61
62 /* Pattern at top of allocated space */
63 #define LBER_MEM_JUNK 0xdeaddadaU
64
65 static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK, 0, 0 };
66
67 /* Note sequence and ber_int_meminuse are counters, but are not
68  * thread safe.  If you want to use these values for multithreaded applications,
69  * you must put mutexes around them, otherwise they will have incorrect values.
70  * When debugging, if you sort the debug output, the sequence number will 
71  * put allocations/frees together.  It is then a simple matter to write a script
72  * to find any allocations that don't have a buffer free function.
73  */
74 #ifdef LDAP_MEMORY_TRACE
75 static ber_int_t sequence = 0;
76 #endif
77
78 /* Pattern placed just before user data */
79 static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde };
80 /* Pattern placed just after user data */
81 static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca };
82
83 #define mbu_len sizeof(ber_int_mem_hdr.ber_align)
84
85 /* Test if pattern placed just before user data is good */
86 #define testdatatop(val) ( \
87         *(val->bm_char+mbu_len-4)==toppattern[0] && \
88         *(val->bm_char+mbu_len-3)==toppattern[1] && \
89         *(val->bm_char+mbu_len-2)==toppattern[2] && \
90         *(val->bm_char+mbu_len-1)==toppattern[3] )
91
92 /* Place pattern just before user data */
93 #define setdatatop(val) *(val->bm_char+mbu_len-4)=toppattern[0]; \
94         *(val->bm_char+mbu_len-3)=toppattern[1]; \
95         *(val->bm_char+mbu_len-2)=toppattern[2]; \
96         *(val->bm_char+mbu_len-1)=toppattern[3];
97
98 /* Test if pattern placed just after user data is good */
99 #define testend(val) (  *((unsigned char *)val+0)==endpattern[0] && \
100         *((unsigned char *)val+1)==endpattern[1] && \
101         *((unsigned char *)val+2)==endpattern[2] && \
102         *((unsigned char *)val+3)==endpattern[3] )
103
104 /* Place pattern just after user data */
105 #define setend(val)     *((unsigned char *)val+0)=endpattern[0]; \
106         *((unsigned char *)val+1)=endpattern[1]; \
107         *((unsigned char *)val+2)=endpattern[2]; \
108         *((unsigned char *)val+3)=endpattern[3];
109
110 #define BER_MEM_BADADDR ((void *) &ber_int_mem_hdr.bm_data)
111 #define BER_MEM_VALID(p)        do { \
112                 assert( (p) != BER_MEM_BADADDR );       \
113                 assert( (p) != (void *) &ber_int_mem_hdr );     \
114         } while(0)
115
116 #else
117 #define BER_MEM_VALID(p)        /* no-op */
118 #endif
119
120 BerMemoryFunctions *ber_int_memory_fns = NULL;
121
122 void
123 ber_memfree_x( void *p, void *ctx )
124 {
125         if( p == NULL ) {
126                 return;
127         }
128
129         BER_MEM_VALID( p );
130
131         if( ber_int_memory_fns == NULL || ctx == NULL ) {
132 #ifdef LDAP_MEMORY_DEBUG
133                 struct ber_mem_hdr *mh = (struct ber_mem_hdr *)
134                         ((char *)p - sizeof(struct ber_mem_hdr));
135                 assert( mh->bm_top == LBER_MEM_JUNK);
136                 assert( testdatatop( mh));
137                 assert( testend( (char *)&mh[1] + mh->bm_length) );
138                 ber_int_meminuse -= mh->bm_length;
139
140 #ifdef LDAP_MEMORY_TRACE
141                 fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n",
142                         (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
143                         ber_int_meminuse);
144 #endif
145                 /* Fill the free space with poison */
146                 memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t));
147                 free( mh );
148 #else
149                 free( p );
150 #endif
151                 return;
152         }
153
154         assert( ber_int_memory_fns->bmf_free != 0 );
155
156         (*ber_int_memory_fns->bmf_free)( p, ctx );
157 }
158
159 void
160 ber_memfree( void *p )
161 {
162         ber_memfree_x(p, NULL);
163 }
164
165 void
166 ber_memvfree_x( void **vec, void *ctx )
167 {
168         int     i;
169
170         if( vec == NULL ) {
171                 return;
172         }
173
174         BER_MEM_VALID( vec );
175
176         for ( i = 0; vec[i] != NULL; i++ ) {
177                 ber_memfree_x( vec[i], ctx );
178         }
179
180         ber_memfree_x( vec, ctx );
181 }
182
183 void
184 ber_memvfree( void **vec )
185 {
186         ber_memvfree_x( vec, NULL );
187 }
188
189 void *
190 ber_memalloc_x( ber_len_t s, void *ctx )
191 {
192         void *new;
193
194 #ifdef LDAP_MEMORY_DEBUG
195         assert( s != 0 );
196 #endif
197
198         if( s == 0 ) {
199                 return NULL;
200         }
201
202         if( ber_int_memory_fns == NULL || ctx == NULL ) {
203 #ifdef LDAP_MEMORY_DEBUG
204                 struct ber_mem_hdr *mh = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t));
205                 if( mh == NULL ) return NULL;
206
207                 mh->bm_top = LBER_MEM_JUNK;
208                 mh->bm_length = s;
209                 setdatatop( mh);
210                 setend( (char *)&mh[1] + mh->bm_length );
211
212                 ber_int_meminuse += mh->bm_length;      /* Count mem inuse */
213
214 #ifdef LDAP_MEMORY_TRACE
215                 mh->bm_sequence = sequence++;
216                 fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n",
217                         (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
218                         ber_int_meminuse);
219 #endif
220                 /* poison new memory */
221                 memset( (char *)&mh[1], 0xff, s);
222
223                 BER_MEM_VALID( &mh[1] );
224                 new = &mh[1];
225 #else
226                 new = malloc( s );
227 #endif
228         } else {
229                 new = (*ber_int_memory_fns->bmf_malloc)( s, ctx );
230         }
231
232         if( new == NULL ) {
233                 ber_errno = LBER_ERROR_MEMORY;
234         }
235
236         return new;
237 }
238
239 void *
240 ber_memalloc( ber_len_t s )
241 {
242         return ber_memalloc_x( s, NULL );
243 }
244
245 void *
246 ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx )
247 {
248         void *new;
249
250 #ifdef LDAP_MEMORY_DEBUG
251         assert( n != 0 && s != 0);
252 #endif
253
254         if( n == 0 || s == 0 ) {
255                 return NULL;
256         }
257
258         if( ber_int_memory_fns == NULL || ctx == NULL ) {
259 #ifdef LDAP_MEMORY_DEBUG
260                 struct ber_mem_hdr *mh = calloc(1,
261                         (n * s) + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) );
262                 if( mh == NULL ) return NULL;
263
264                 mh->bm_top = LBER_MEM_JUNK;
265                 mh->bm_length = n*s;
266                 setdatatop( mh);
267                 setend( (char *)&mh[1] + mh->bm_length );
268
269                 ber_int_meminuse += mh->bm_length;
270
271 #ifdef LDAP_MEMORY_TRACE
272                 mh->bm_sequence = sequence++;
273                 fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n",
274                         (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
275                         ber_int_meminuse);
276 #endif
277                 BER_MEM_VALID( &mh[1] );
278                 new = &mh[1];
279 #else
280                 new = calloc( n, s );
281 #endif
282
283         } else {
284                 new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx );
285         }
286
287         if( new == NULL ) {
288                 ber_errno = LBER_ERROR_MEMORY;
289         }
290
291         return new;
292 }
293
294 void *
295 ber_memcalloc( ber_len_t n, ber_len_t s )
296 {
297         return ber_memcalloc_x( n, s, NULL );
298 }
299
300 void *
301 ber_memrealloc_x( void* p, ber_len_t s, void *ctx )
302 {
303         void *new = NULL;
304
305         /* realloc(NULL,s) -> malloc(s) */
306         if( p == NULL ) {
307                 return ber_memalloc_x( s, ctx );
308         }
309         
310         /* realloc(p,0) -> free(p) */
311         if( s == 0 ) {
312                 ber_memfree_x( p, ctx );
313                 return NULL;
314         }
315
316         BER_MEM_VALID( p );
317
318         if( ber_int_memory_fns == NULL || ctx == NULL ) {
319 #ifdef LDAP_MEMORY_DEBUG
320                 ber_int_t oldlen;
321                 struct ber_mem_hdr *mh = (struct ber_mem_hdr *)
322                         ((char *)p - sizeof(struct ber_mem_hdr));
323                 assert( mh->bm_top == LBER_MEM_JUNK);
324                 assert( testdatatop( mh));
325                 assert( testend( (char *)&mh[1] + mh->bm_length) );
326                 oldlen = mh->bm_length;
327
328                 p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) );
329                 if( p == NULL ) {
330                         ber_errno = LBER_ERROR_MEMORY;
331                         return NULL;
332                 }
333
334                         mh = p;
335                 mh->bm_length = s;
336                 setend( (char *)&mh[1] + mh->bm_length );
337                 if( s > oldlen ) {
338                         /* poison any new memory */
339                         memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen);
340                 }
341
342                 assert( mh->bm_top == LBER_MEM_JUNK);
343                 assert( testdatatop( mh));
344
345                 ber_int_meminuse += s - oldlen;
346 #ifdef LDAP_MEMORY_TRACE
347                 fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n",
348                         (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
349                         ber_int_meminuse);
350 #endif
351                         BER_MEM_VALID( &mh[1] );
352                 return &mh[1];
353 #else
354                 new = realloc( p, s );
355 #endif
356         } else {
357                 new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx );
358         }
359
360         if( new == NULL ) {
361                 ber_errno = LBER_ERROR_MEMORY;
362         }
363
364         return new;
365 }
366
367 void *
368 ber_memrealloc( void* p, ber_len_t s )
369 {
370         return ber_memrealloc_x( p, s, NULL );
371 }
372
373 void
374 ber_bvfree_x( struct berval *bv, void *ctx )
375 {
376         if( bv == NULL ) {
377                 return;
378         }
379
380         BER_MEM_VALID( bv );
381
382         if ( bv->bv_val != NULL ) {
383                 ber_memfree_x( bv->bv_val, ctx );
384         }
385
386         ber_memfree_x( (char *) bv, ctx );
387 }
388
389 void
390 ber_bvfree( struct berval *bv )
391 {
392         ber_bvfree_x( bv, NULL );
393 }
394
395 void
396 ber_bvecfree_x( struct berval **bv, void *ctx )
397 {
398         int     i;
399
400         if( bv == NULL ) {
401                 return;
402         }
403
404         BER_MEM_VALID( bv );
405
406         /* count elements */
407         for ( i = 0; bv[i] != NULL; i++ ) ;
408
409         /* free in reverse order */
410         for ( i--; i >= 0; i-- ) {
411                 ber_bvfree_x( bv[i], ctx );
412         }
413
414         ber_memfree_x( (char *) bv, ctx );
415 }
416
417 void
418 ber_bvecfree( struct berval **bv )
419 {
420         ber_bvecfree_x( bv, NULL );
421 }
422
423 int
424 ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx )
425 {
426         ber_len_t i;
427         struct berval **new;
428
429         if( *bvec == NULL ) {
430                 if( bv == NULL ) {
431                         /* nothing to add */
432                         return 0;
433                 }
434
435                 *bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx );
436
437                 if( *bvec == NULL ) {
438                         return -1;
439                 }
440
441                 (*bvec)[0] = bv;
442                 (*bvec)[1] = NULL;
443
444                 return 1;
445         }
446
447         BER_MEM_VALID( bvec );
448
449         /* count entries */
450         for ( i = 0; (*bvec)[i] != NULL; i++ ) {
451                 /* EMPTY */;
452         }
453
454         if( bv == NULL ) {
455                 return i;
456         }
457
458         new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx);
459
460         if( new == NULL ) {
461                 return -1;
462         }
463
464         *bvec = new;
465
466         (*bvec)[i++] = bv;
467         (*bvec)[i] = NULL;
468
469         return i;
470 }
471
472 int
473 ber_bvecadd( struct berval ***bvec, struct berval *bv )
474 {
475         return ber_bvecadd_x( bvec, bv, NULL );
476 }
477
478 struct berval *
479 ber_dupbv_x(
480         struct berval *dst, struct berval *src, void *ctx )
481 {
482         struct berval *new;
483
484         if( src == NULL ) {
485                 ber_errno = LBER_ERROR_PARAM;
486                 return NULL;
487         }
488
489         if ( dst ) {
490                 new = dst;
491         } else {
492                 if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
493                         ber_errno = LBER_ERROR_MEMORY;
494                         return NULL;
495                 }
496         }
497
498         if ( src->bv_val == NULL ) {
499                 new->bv_val = NULL;
500                 new->bv_len = 0;
501                 return new;
502         }
503
504         if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) {
505                 ber_errno = LBER_ERROR_MEMORY;
506                 if ( !dst )
507                         ber_memfree_x( new, ctx );
508                 return NULL;
509         }
510
511         AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len );
512         new->bv_val[src->bv_len] = '\0';
513         new->bv_len = src->bv_len;
514
515         return new;
516 }
517
518 struct berval *
519 ber_dupbv(
520         struct berval *dst, struct berval *src )
521 {
522         return ber_dupbv_x( dst, src, NULL );
523 }
524
525 struct berval *
526 ber_bvdup(
527         struct berval *src )
528 {
529         return ber_dupbv_x( NULL, src, NULL );
530 }
531
532 struct berval *
533 ber_str2bv_x(
534         LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv,
535         void *ctx)
536 {
537         struct berval *new;
538
539         if( s == NULL ) {
540                 ber_errno = LBER_ERROR_PARAM;
541                 return NULL;
542         }
543
544         if( bv ) {
545                 new = bv;
546         } else {
547                 if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
548                         ber_errno = LBER_ERROR_MEMORY;
549                         return NULL;
550                 }
551         }
552
553         new->bv_len = len ? len : strlen( s );
554         if ( dup ) {
555                 if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) {
556                         ber_errno = LBER_ERROR_MEMORY;
557                         if ( !bv )
558                                 ber_memfree_x( new, ctx );
559                         return NULL;
560                 }
561
562                 AC_MEMCPY( new->bv_val, s, new->bv_len );
563                 new->bv_val[new->bv_len] = '\0';
564         } else {
565                 new->bv_val = (char *) s;
566         }
567
568         return( new );
569 }
570
571 struct berval *
572 ber_str2bv(
573         LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv)
574 {
575         return ber_str2bv_x( s, len, dup, bv, NULL );
576 }
577
578 struct berval *
579 ber_mem2bv_x(
580         LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv,
581         void *ctx)
582 {
583         struct berval *new;
584
585         if( s == NULL ) {
586                 ber_errno = LBER_ERROR_PARAM;
587                 return NULL;
588         }
589
590         if( bv ) {
591                 new = bv;
592         } else {
593                 if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
594                         ber_errno = LBER_ERROR_MEMORY;
595                         return NULL;
596                 }
597         }
598
599         new->bv_len = len;
600         if ( dup ) {
601                 if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) {
602                         ber_errno = LBER_ERROR_MEMORY;
603                         if ( !bv ) {
604                                 ber_memfree_x( new, ctx );
605                         }
606                         return NULL;
607                 }
608
609                 AC_MEMCPY( new->bv_val, s, new->bv_len );
610                 new->bv_val[new->bv_len] = '\0';
611         } else {
612                 new->bv_val = (char *) s;
613         }
614
615         return( new );
616 }
617
618 struct berval *
619 ber_mem2bv(
620         LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv)
621 {
622         return ber_mem2bv_x( s, len, dup, bv, NULL );
623 }
624
625 char *
626 ber_strdup_x( LDAP_CONST char *s, void *ctx )
627 {
628         char    *p;
629         size_t  len;
630         
631 #ifdef LDAP_MEMORY_DEBUG
632         assert(s != NULL);                      /* bv damn better point to something */
633 #endif
634
635         if( s == NULL ) {
636                 ber_errno = LBER_ERROR_PARAM;
637                 return NULL;
638         }
639
640         len = strlen( s ) + 1;
641
642         if ( (p = ber_memalloc_x( len, ctx )) == NULL ) {
643                 ber_errno = LBER_ERROR_MEMORY;
644                 return NULL;
645         }
646
647         AC_MEMCPY( p, s, len );
648         return p;
649 }
650
651 char *
652 ber_strdup( LDAP_CONST char *s )
653 {
654         return ber_strdup_x( s, NULL );
655 }
656
657 char *
658 ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx )
659 {
660         char    *p;
661         size_t  len;
662         
663 #ifdef LDAP_MEMORY_DEBUG
664         assert(s != NULL);                      /* bv damn better point to something */
665 #endif
666
667         if( s == NULL ) {
668                 ber_errno = LBER_ERROR_PARAM;
669                 return NULL;
670         }
671
672         len = strlen( s );
673
674         if ( len > l ) {
675                 len = l;
676         }
677
678         if ( (p = ber_memalloc_x( len + 1, ctx )) == NULL ) {
679                 ber_errno = LBER_ERROR_MEMORY;
680                 return NULL;
681         }
682
683         AC_MEMCPY( p, s, len );
684         p[len] = '\0';
685         return p;
686 }
687
688 char *
689 ber_strndup( LDAP_CONST char *s, ber_len_t l )
690 {
691         return ber_strndup_x( s, l, NULL );
692 }
693
694 /*
695  * dst is resized as required by src and the value of src is copied into dst
696  * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be
697  * alloc'ed with the context ctx
698  */
699 struct berval *
700 ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx )
701 {
702         assert( dst != NULL );
703
704         if ( dst->bv_len < src->bv_len ) {
705                 dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx );
706         }
707
708         AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 );
709         dst->bv_len = src->bv_len;
710
711         return dst;
712 }
713
714 struct berval *
715 ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src )
716 {
717         return ber_bvreplace_x( dst, src, NULL );
718 }
719
720 void
721 ber_bvarray_free_x( BerVarray a, void *ctx )
722 {
723         int i;
724
725         if (a) {
726                 BER_MEM_VALID( a );
727
728                 /* count elements */
729                 for (i=0; a[i].bv_val; i++) ;
730                 
731                 /* free in reverse order */
732                 for (i--; i>=0; i--) {
733                         ber_memfree_x(a[i].bv_val, ctx);
734                 }
735
736                 ber_memfree_x(a, ctx);
737         }
738 }
739
740 void
741 ber_bvarray_free( BerVarray a )
742 {
743         ber_bvarray_free_x(a, NULL);
744 }
745
746 int
747 ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx )
748 {
749         int     n;
750
751         if ( *a == NULL ) {
752                 if (bv == NULL) {
753                         return 0;
754                 }
755                 n = 0;
756
757                 *a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx );
758                 if ( *a == NULL ) {
759                         return -1;
760                 }
761
762         } else {
763                 BerVarray atmp;
764                 BER_MEM_VALID( a );
765
766                 for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) {
767                         ;       /* just count them */
768                 }
769
770                 if (bv == NULL) {
771                         return n;
772                 }
773
774                 atmp = (BerValue *) ber_memrealloc_x( (char *) *a,
775                     (n + 2) * sizeof(BerValue), ctx );
776
777                 if( atmp == NULL ) {
778                         return -1;
779                 }
780
781                 *a = atmp;
782         }
783
784         (*a)[n++] = *bv;
785         (*a)[n].bv_val = NULL;
786
787         return n;
788 }
789
790 int
791 ber_bvarray_add( BerVarray *a, BerValue *bv )
792 {
793         return ber_bvarray_add_x( a, bv, NULL );
794 }