3 堆栈

man DEFINE_STACK_OF

3.1 openssl堆栈

堆栈是一种先进后出的数据结构。 openssl大量采用堆栈来存放数据。
它实现了一个通用的堆栈,可以方便的存储任意数据。

它实现了许多基本的堆栈操作,主要有:
    构建新堆栈(sk_new_null,sk_new)、
    堆栈拷贝(sk_dup)、
    插入数据(sk_insert)、 删除数据(sk_delete)、
    查找数据(sk_find,sk_find_ex)、
    入栈(sk_push)、 出栈(sk_pop)、
    获取堆栈元素个数(sk_num)、 获取堆栈值(sk_value)、
    设置堆栈值(sk_set)和堆栈排序(sk_sort)。
// 通用堆栈

//创建一个空栈,参数可指定排序方法,因为openssl不知道里面存放的是什么类型的数据,
// 所以排序方法需要用户实现,当参数为NULL,同下方法
OPENSSL_STACK *OPENSSL_sk_new(OPENSSL_sk_compfunc cmp);

//创建一个空栈,
OPENSSL_STACK *OPENSSL_sk_new_null(void);

//释放栈,并不释放栈内元素内存
void OPENSSL_sk_free(OPENSSL_STACK *);

//删除并释放所有栈内元素,最后释放栈,可以指定回调函数,栈每次释放一个元素都会回调该回调函数
void OPENSSL_sk_pop_free(OPENSSL_STACK *st, void (*func) (void *));

//栈深copy,
OPENSSL_STACK *OPENSSL_sk_deep_copy( const OPENSSL_STACK *,
                                     OPENSSL_sk_copyfunc c,
                                     OPENSSL_sk_freefunc f);

//在栈指定位置插入元素,成功返回该栈所有元素的个数
int OPENSSL_sk_insert(OPENSSL_STACK *sk, const void *data, int where);

//删除栈指定位置元素,成功返回删除的该元素
void *OPENSSL_sk_delete(OPENSSL_STACK *st, int loc);

//删除栈指定元素,成功返回删除的该元素
void *OPENSSL_sk_delete_ptr(OPENSSL_STACK *st, const void *p);

//在栈中查找指元素,成功返回该元素位置
int OPENSSL_sk_find(OPENSSL_STACK *st, const void *data);

//同上,
int OPENSSL_sk_find_ex(OPENSSL_STACK *st, const void *data);

//在栈顶添加一个元素,成功返回栈元素总数
int OPENSSL_sk_push(OPENSSL_STACK *st, const void *data);

//在栈位置0次添加一个元素,类似 insert(st,0);
int OPENSSL_sk_unshift(OPENSSL_STACK *st, const void *data);

//移出栈位置0处的元素,类似pop
void *OPENSSL_sk_shift(OPENSSL_STACK *st);

//在栈顶移出一个元素,并释放该元素内存,
void *OPENSSL_sk_pop(OPENSSL_STACK *st);

//设置栈元素为0,不释放栈
void OPENSSL_sk_zero(OPENSSL_STACK *st);

//设置栈的排序方法
OPENSSL_sk_compfunc OPENSSL_sk_set_cmp_func( OPENSSL_STACK *sk,
                                             OPENSSL_sk_compfunc cmp);

//copy 栈
OPENSSL_STACK *OPENSSL_sk_dup(const OPENSSL_STACK *st);

//栈排序
void OPENSSL_sk_sort(OPENSSL_STACK *st);

//栈是否排序
int OPENSSL_sk_is_sorted(const OPENSSL_STACK *st);

3.2 数据结构

openssl堆栈数据结构在stack.h中定义如下:
typedef struct stack_st
{
       int num;
       char **data;
       int sorted;
       int num_alloc;
       int (*comp)(const char * const *, const char * const *);
} STACK;

各项意义如下:
    num :    堆栈中存放数据的个数。
    data :   用于存放数据地址,每个数据地址存放在data[0]到data[num-1]中。
    sorted : 堆栈是否已排序,如果排序则值为1,否则为0,堆栈数据一般是无序的,
             只有当用户调用了sk_sort操作,其值才为1。
    comp :   堆栈内存放数据的比较函数地址,此函数用于排序和查找操作;
             当用户生成一个新堆栈时,可以指定comp为用户实现的一个比较函数;
             或当堆栈已经存在时通过调用sk_set_cmp_func函数来重新指定比较函数。

   注意,用户不需要调用底层的堆栈函数(sk_sort、sk_set_cmp_func等),而是调用他通过宏实现的各个函数。

3.3 源码

openssl堆栈实现源码位于crypto/stack目录下。下面分析了部分函数。

1)  sk_set_cmp_func
    此函数用于设置堆栈存放数据的比较函数。
    由于堆栈不知道用户存放的是什么数据,
    所以,比较函数必须由用户自己实现。

2)  sk_find
    根据数据地址来查找它在堆栈中的位置。
    当堆栈设置了比较函数时,它首先对堆栈进行排序,然后通过二分法进行查找。
    如果堆栈没有设置比较函数,它只是简单的比较数据地址来查找.

3)sk_sort
   本函数对堆栈数据排序。
   它首先根据sorted来判断是否已经排序,
   如果未排序则调用了标准C函数qsort进行快速排序。

4)sk_pop_free
   本函数用于释放堆栈内存放的数据以及堆栈本身,
   它需要一个由用户指定的针对具体数据的释放函数。
   如果用户仅调用sk_free函数,则只会释放堆栈本身所用的内存,而不会释放数据内存。

3.4 定义用户自己的堆栈

STACK_OF(TYPE)
DEFINE_STACK_OF(TYPE)
DEFINE_STACK_OF_CONST(TYPE)
DEFINE_SPECIAL_STACK_OF(FUNCTYPE, TYPE)
DEFINE_SPECIAL_STACK_OF_CONST(FUNCTYPE, TYPE)

/*
  test/stack_test.c
*/

3.5 编程示例

3.5.1 通用堆栈

#include <stdio.h>
#include <openssl/safestack.h>


void popfreeCallBack(void *arg)
{
	printf("pop and free  %d \n",*(int*)arg);
}

/* ****************
 * 通用链表 
 ***************** */
int  test_stack()
{
    //创建一个空的栈
    OPENSSL_STACK *st = OPENSSL_sk_new_null();

    int a = 10;
    OPENSSL_sk_push(st, &a);

    int b = 100;
    OPENSSL_sk_push(st, &b);

    char *c = "hello";
    OPENSSL_sk_push(st, c);

    char d[10] = {0};
    OPENSSL_sk_push(st, d);

    char e = 'A';
    OPENSSL_sk_push(st, &e);

    //返回栈内数据个数
    if (OPENSSL_sk_num(st) == 5) {
        printf("sk_num PASS\n");
    }

    //获取指定index数据
    int *getb = OPENSSL_sk_value(st, 1);
    if (*getb==b) {
        printf("sk_value PASS\n");
    }

    //获取指定数据,返回index
    if (OPENSSL_sk_find(st,"hello") == 2) {
        printf("sk_find PASS \n");
    }

    //在位置2插入一个 数据 TAOBAO,返回总数
    if (OPENSSL_sk_insert(st, "TAOBAO", 2) == 6) {
        printf("sk_insert PASS\n");
    }

    int ff = 88;
    OPENSSL_sk_push(st, &ff);
    //在栈顶移出一个数据, 返回删除的元素
    char *popDT = OPENSSL_sk_pop(st);
    if (*popDT==ff) {
        printf("sk_pop PASS\n");
    }

    //从栈中移出所有的元素,并释放内存,并且释放st;
    //每删除一个元素,回调一次popfreeCallBack回调函数
    OPENSSL_sk_pop_free(st, popfreeCallBack);
    return 0;
}

int main()
{
	test_stack(); // 通用链表
	return 0;
}

3.5.2 自定义堆栈

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/safestack.h>

//#define sk_Student_new(st) OPENSSL_sk_new(st)
//#define sk_Student_new_null() OPENSSL_sk_new_null()
//#define sk_Student_free(st) OPENSSL_sk_free(st)
//#define sk_Student_num(st) OPENSSL_sk_num(st)
//#define sk_Student_value(st, i) OPENSSL_sk_value((st), i)
//#define sk_Student_set(st, i, val) OPENSSL_sk_set((st), (i), (val))
//#define sk_Student_zero(st) OPENSSL_sk_zero(Student, (st))
//#define sk_Student_push(st, val) OPENSSL_sk_push((st),(val))
//#define sk_Student_unshift(st, val) OPENSSL_sk_unshift((st), (val))
//#define sk_Student_find(st, val) OPENSSL_sk_find((st), (val))
//#define sk_Student_delete(st, i) OPENSSL_sk_delete((st), (i))
//#define sk_Student_delete_ptr(st, ptr) OPENSSL_sk_delete_ptr((st), (ptr))
//#define sk_Student_insert(st, val, i) OPENSSL_sk_insert((st), (val), (i))
//#define sk_Student_set_cmp_func(st, cmp) OPENSSL_sk_set_cmp_func((st), (cmp))
//#define sk_Student_dup(st) OPENSSL_sk_dup(Student, st)
//#define sk_Student_pop_free(st, free_func) OPENSSL_sk_pop_free((st), (free_func))
//#define sk_Student_shift(st) OPENSSL_sk_shift(st)
//#define sk_Student_pop(st) OPENSSL_sk_pop(st)
//#define sk_Student_sort(st) OPENSSL_sk_sort(st)


typedef struct Student_st {
	char  *name;
	int   age;
	char  *otherInfo;
}Student;

typedef STACK_OF(Student) Students;

DEFINE_STACK_OF(Student)  // 重点

Student *Student_Malloc()
{
	Student *a=malloc(sizeof(Student));
	a->name=malloc(20);
	strcpy(a->name,"zcp");
	a->otherInfo=malloc(20);
	strcpy(a->otherInfo,"no info");
	return a;
}

void Student_Free(Student *a)
{
	free(a->name);
	free(a->otherInfo);
	free(a);
}

static int Student_cmp(const Student * const *a, const Student  *const *b)
{
	int  ret;
	printf("%s cpmp %s \n",(*a)->name,(*b)->name);
	ret=strcmp((*a)->name,(*b)->name);
	return ret;
}

int main()
{

	Students *s, *snew;
	Student  *s1, *one, *s2;
	int      i, num;

	s = sk_Student_new_null();
	//snew = sk_Student_new((sk_Student_compfunc)Student_cmp);
	snew = sk_Student_new(Student_cmp);
	
	s2=Student_Malloc();
	sk_Student_push(snew,s2);
	i=sk_Student_find(snew,s2);
	printf("at : %d \n" ,i  );
	
	s1=Student_Malloc();
	sk_Student_push(s,s1);

	num=sk_Student_num(s);
	for(i=0;i<num;i++) {
		one=sk_Student_value(s,i);
		printf("student name :    %s\n",one->name);
		printf("sutdent age  :     %d\n",one->age);
		printf("student otherinfo :      %s\n\n",one->otherInfo);
	}
	sk_Student_pop_free(s,Student_Free);
	sk_Student_pop_free(snew,Student_Free);
	return 0;
}