[postgreSQL,c++] PostgreSQL源码分析_StringInfo_primary

来源:互联网 发布:js调用手机图库 编辑:程序博客网 时间:2024/04/23 18:05

最近的文章主要是关于 PostgreSQL 方向的源代码的学习与使用的,

分析的版本是 postgresql-8.4.1

下载地址是:http://www.postgresql.org/ftp/source/v8.4.1


每一份源码文件主要将分成两篇来进行:

一篇中主要内容是阅读、注释经典的PostgreSQL内核中的源代码,也就是所谓的初级篇Primary。

第二篇中主要内容是在熟练使用前一篇中所提到的 unix/linux  API 的基础之上,对该API进行各种实验测试,

并结合前一篇所介绍的经典内核代码来进行修改按照自己的想法实现一些小的变动,并对该代码进行编译与试运行。


--------------------------------------------------------------------------------------

文件名称:stringinfo.h stringinfo.c

文件所在目录:

stringinfo.h :   ..\src\include\lib

stringinfo.c :  .. \src\backend\lib


----------------------------------------------------------------------------------------

stringinfo.h

首先来看文档的注释部分:

/*------------------------------------------------------------------------- * * stringinfo.h *  Declarations/definitions for "StringInfo" functions. * * StringInfo provides an indefinitely-extensible string data type. * It can be used to buffer either ordinary C strings (null-terminated text) * or arbitrary binary data.  All storage is allocated with palloc(). * * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL: pgsql/src/include/lib/stringinfo.h,v 1.36 2009/01/01 17:23:59 momjian Exp $ * *------------------------------------------------------------------------- */

stringinfo.h 

在这篇文档中主要记录了关于 StringInfo 对象及基于该对象的操作方法的声明、定义这些信息。

StringInfo 这个数据对象提供了不明确的可扩展式的串数据类型。

它既可以被用来作为普通 C 语言中的字符串来使用,也可以用来存放任意的二进制数据。

无论是用来存放哪一种类型,其内存空间的划分和分配都是通过调用 palloc() 这个函数来实现的。



接下来是关于StringInfo 这个数据对象的声明信息

#ifndef STRINGINFO_H#define STRINGINFO_H/*------------------------- * StringInfoData holds information about an extensible string. *datais the current buffer for the string (allocated with palloc). *lenis the current string length.  There is guaranteed to be *a terminating '\0' at data[len], although this is not very *useful when the string holds binary data rather than text. *maxlenis the allocated size in bytes of 'data', i.e. the maximum *string size (including the terminating '\0' char) that we can *currently store in 'data' without having to reallocate *more space.  We must always have maxlen > len. *cursoris initialized to zero by makeStringInfo or initStringInfo, *but is not otherwise touched by the stringinfo.c routines. *Some routines use it to scan through a StringInfo. *------------------------- */typedef struct StringInfoData{char   *data;intlen;intmaxlen;intcursor;} StringInfoData;typedef StringInfoData *StringInfo;

在一开始的宏定义是为了避免该头文件因为被多个文件引用而在编译的时候被编译多次的情况发生。

接下来是对StringInfoData 结构体中所要定义的各个属性字段进行一一说明,其中:


data: 是当前该字串中所包含的字符的指针,指向一块有 palloc () 函数开辟的空间,空间里面存放着

StringInfoData 对象中所包含的字符串。


len:这个变量用于记录当前包含的字串的长度,根据规定在结构体字符串数组 data的最后一位,

即 data[len] 中存放的字符是 '\0' , 用来标定当前字符到此结束。当然,如果你使用data来存放二进制

数据的话,这种表示方式用途也不大。


maxlen: 这个变量是用来描述在创建 StringInfoData 对象的时候划分给data 变量实际的空间大小,也就是说,

在我们不调用 reallocate 方法进行内存重新分配的前提下,StringInfoData 对象中data 可以存放字符的

最大容量是多少,(其中将最后一位的 '\0' 的存储考虑在内)。

为了确保程序的正确性,我们必须要保证StringInfoData 中的 maxlen 字段总是大于len 字段。


cursor: 结构体中的这个变量字段在对结构体变量 StringInfoData 调用 makeStringInfo 或是 initStringInfo 

方法任意一个时,会将该字段的数值进行清零。


通过后续的 typedef 我们可以知道,StringInfo 是用来指向 StringInfoData 的指针变量。



接下来是对StringInfo 对象的创建方法进行了说明,不得不说,这里的文档描述的十分的清晰详细。


/*------------------------ * There are two ways to create a StringInfo object initially: * * StringInfo stringptr = makeStringInfo(); *Both the StringInfoData and the data buffer are palloc'd. * * StringInfoData string; * initStringInfo(&string); *The data buffer is palloc'd but the StringInfoData is just local. *This is the easiest approach for a StringInfo object that will *only live as long as the current routine. * * To destroy a StringInfo, pfree() the data buffer, and then pfree() the * StringInfoData if it was palloc'd.  There's no special support for this. * * NOTE: some routines build up a string using StringInfo, and then * release the StringInfoData but return the data string itself to their * caller.At that point the data string looks like a plain palloc'd * string. *------------------------- */

创建初始的StringInfo 对象一共有两种方法:

一种是通过

StringInfo stringptr = makeStringInfo() ;

这个方法来进行创建,使用该方法创建的 StringInfoData 对象无论是StringInfoData 对象,

还是对象中的 char *data 字段所占用的空间都是通过 palloc 这个函数来进行分配的。

这里的意思就是说,创建 StringInfoData 一共有两种方式:

一种是通过系统自动为其分配对象存储空间。

另一种是通过程序员自己手动来进行对象内存的划分。

不过这里所说的系统自动和手动分配内存必须要更加的详细说明一下才可以。

现在,我们要创建一个StringInfoData 的对象,该对象中包含着另一个对象 char *data ,


第一种方式,即上面所介绍的方式,是使用手动分配内存的方式来分配 StringInfoData 和 char *data 两个对象。

第二种方式,即下面将要介绍的方式,是使用手动的分配方式来分配 StringInfoData 中的 char *data 这一个对象,

而StringInfoData 对象的分配是通过系统自动分配内存空间的方式来创建的。

(just local 就是说仅仅是创建一个本地对象,用后方法退出占用的内存就被系统自动回收)。


至于手动和系统自动分配内存空间这一点就不详细说明了,二者的区别就相当于是有一个 class A ;

分别使用

A *a= new A() ;A  a ;

这两种分配方式,

第一种在程序结束的时候需要手动的将内存进行释放, delete a; 如果处理不当,会造成内存泄露。

而第二种内存空间的分配方式,在方法结束的时候会自动的调用 class A 中的析构方法进行空间释放,

也就是所谓的系统自动收回空间。

第二种方式是通过:


StringInfoData string;initStringInfo(&string);

来创建对象的,其实从代码表述上即可知道,首先创建一个实体对象,

然后将该对象以引用的方式传入到 initStringInfo 方法中,这样做的目的是调用相关的方法对新创建好的对象中的属性字段进行初始化操作,

其中也包括调用 palloc 方法来对string.data 这一进行空间的分配。


并且需要注意的的一点就是,对于第一种使用到系统堆栈分配的变量,在需要释放掉其占用的内存空间的时候,

需要调用 pfree 函数来手动释放空间。

因为我们知道,使用 delete 调用的是该对象类的析构函数,或是对一个且某一种基本类型的堆栈空间进行释放,

如果想要手动释放结构体变量所占用的内存空间的话,就必须要使用相配套的destory 方法。


第二种创建对象的方法的高明之处在文档中也进行了介绍,即便是在本地方法中以第二种方式创建一个 StringInfoData 的对象,在该子方法执行

结束之后,StringInfoData 由于是本地变量而随着方法的结束而将占用的内存空间由系统进行回收。但是,StringInfoData 对象中的 char *data ;

对象却因为是由 palloc 分配系统堆栈上面的空间而被保留,以至于可以作为返回值将其数值返回给调用方法的主调函数中。

这一功能与C++ 中的string对象可以直接作为返回值被主调函数接收是一样的。



下面开始介绍基于 StringInfo 对象的操作函数的声明及其功能

/*------------------------ * makeStringInfo * Create an empty 'StringInfoData' & return a pointer to it. */extern StringInfo makeStringInfo(void);

创建一个 StringInfoData 的数据对象,并将指向该对象的指针(StringInfo 类型)作为返回值返回。
这种创建对象的方法,在自己编写相关数据结构程序的时候,是十分有借鉴性的。



/*------------------------ * initStringInfo * Initialize a StringInfoData struct (with previously undefined contents) * to describe an empty string. */extern void initStringInfo(StringInfo str);

这个方法是将指向一个创建好的 StringInfoData 对象的指针传入至方法中,然后通过指针来对该对象进行一系列的初始化操作。



/*------------------------ * resetStringInfo * Clears the current content of the StringInfo, if any. The * StringInfo remains valid. */extern void resetStringInfo(StringInfo str);

这个方法是通过指向 StringInfoData 的指针来将该对象中的当前包含的内容信息全部清空,
但是并不销毁传入的 StringInfo 指针。



/*------------------------ * appendStringInfo * Format text data under the control of fmt (an sprintf-style format string) * and append it to whatever is already in str.  More space is allocated * to str if necessary.  This is sort of like a combination of sprintf and * strcat. */extern voidappendStringInfo(StringInfo str, const char *fmt,...)
该方法实现来将传入的StringInfo指针指向的StringInfoData 对象中的内容根据后面自定义的格式,

追加到当前StringInfoData 对象的char *data 字段上面。 如果内存不足的话,可以实现为其分配新的存储空间的功能。

该方法实现有点类似于将 sprintf 和strcat 两个函数的功能合并起来实现的。



/* This extension allows gcc to check the format string */__attribute__((format(printf, 2, 3)));

上述的这种方法的扩展允许 gcc 编译器来对格式化的 string 对象进行检查。


/*------------------------ * appendStringInfoVA * Attempt to format text data under the control of fmt (an sprintf-style * format string) and append it to whatever is already in str.If successful * return true; if not (because there's not enough space), return false * without modifying str.  Typically the caller would enlarge str and retry * on false return --- see appendStringInfo for standard usage pattern. */extern bool appendStringInfoVA(StringInfo str, const char *fmt, va_list args);

上述的这种方法可以实现以固定格式的方式将内容追加到 StringInfoData 对象的原有 char * data 字段中。
如果追加操作成功,则返回 true,否则的话返回 false(原因是由于内存不足,所以追加失败) 。
与上述的操作不同,如果内存不足的话直接返回 false 而并不对StringInfoData.data 这一字段进行修改。
调用者通常的做法就是,在调用该函数返回false之后将 str 的大小调大,然后再次调用该函数。



/*------------------------ * appendStringInfoString * Append a null-terminated string to str. * Like appendStringInfo(str, "%s", s) but faster. */extern void appendStringInfoString(StringInfo str, const char *s);

上述方法的功能是将一个非空的字符串追加到另一个字符串的后面,
就像是调用 appendStringInfo (str, "%s" , s) 的功能一样(该方法实现了将在字符串 str 的后面以字符串的格式
追加变量 s 。上述的方法只是将情况特殊化了。因为完全可以使用 appendStringInfo(str, "%d" , 333) ; 这种方式
来将数字追加到str的后面。




/*------------------------ * appendStringInfoChar * Append a single byte to str. * Like appendStringInfo(str, "%c", ch) but much faster. */extern void appendStringInfoChar(StringInfo str, char ch);

上述方法实现的是,将一个字节追加到一个字符串的后面,其功能就相当于是以 %c 的格式来调用
appendStringInfo 这个方法一样,即
appendStringInfo(str , “%c” , ch ) ,该方法由于无需对输入参数进行格式化而使得方法的执行速度加快。


/*------------------------ * appendStringInfoCharMacro * As above, but a macro for even more speed where it matters. * Caution: str argument will be evaluated multiple times. */#define appendStringInfoCharMacro(str,ch) \(((str)->len + 1 >= (str)->maxlen) ? \ appendStringInfoChar(str, ch) : \ (void)((str)->data[(str)->len] = (ch), (str)->data[++(str)->len] = '\0'))

上述的方法是以宏的方式来实现在一个字符串的后面追加一个字符的操作,宏操作的速度通常会快很多。
不过在这里会将宏的操作分为两个方向执行,

如果内存空间足够则直接调用宏来进行字串中元素的追加以及字串中最后一位的 '\0' 结尾标志的修改。

如果内存空间不足,需要通过重新追加新的内存,宏操作还是处理不了如此复杂的情况的,所以只能够转到

上面的 appendStringInfoChar(StringInfo , char ) 这个方法来执行了。

这个方法中包含了对内存空间不够会重新分配内存这种情况的处理。


/*------------------------ * appendBinaryStringInfo * Append arbitrary binary data to a StringInfo, allocating more space * if necessary. */extern void appendBinaryStringInfo(StringInfo str,   const char *data, int datalen);/*------------------------ * enlargeStringInfo * Make sure a StringInfo's buffer can hold at least 'needed' more bytes. */extern void enlargeStringInfo(StringInfo str, int needed);#endif   /* STRINGINFO_H */
上述的两种方法比较容易理解,其中 appendBinaryStringInfo 这个方法主要功能是在一个StringInfo 对象的data(char*)

的后面追加长度为 datalen 内容是由 char *data 指向的二进制数据。


而enlargeStringInfo 这个方法的功能是将StringInfo 中的 data(char*) 所指向的空间在原有的基础上进行扩大,

同时不能够忘了将maxlen 的数值进行更新。扩大,应该扩大到多少呢? 根据传入的参数 needed 的数值,

在原有的基础上至少扩大 needed 这么多的字节。



明天在今天的基础上,开始分析stringinfo.c 中的源代码。

0 0