ruby049源码分析_string.c文件
昨天写了array.c的文件中各函数的解读,下班后,在床上就进行回顾。发现真正写一遍后,自己思维更清楚了。就是说,象一本书《59秒》所言,倾说无益,书写才有效果。把知识点自己写下来,自己理解得更深入了。
所以今天接着来分析string.c文件,因为字符串我使用得也较多,算是稍微熟悉一些。
一、初始化函数
先看结构体
struct RString {
struct RBasic basic;
UINT len;
char *ptr;
struct RString *orig;
};
其中len存储字串的长度,*ptr指向字串的具体位置,这两个都好理解,只有orig指针不懂。感觉从心理上无法理解。
好像awk中,字串的定义也比较怪。
struct {
char *sp;
short slen,sref;
} str;
AWKNUM fltnum;
} sub;
} NODE;
我把awk.h文件中,关于字串的定义弄出来。其中sref什么意思?我不明白。不想了。看函数吧。
VALUE
str_new(ptr, len)
char *ptr;
UINT len;
{
NEWOBJ(str, struct RString);
OBJSETUP(str, C_String, T_STRING);
str->len = len;
str->ptr = ALLOC_N(char,len+1);
if (ptr) {
memmove(str->ptr, ptr, len);
}
str->ptr[len] = '\0';
str->orig = Qnil;
return (VALUE)str;
}
函数写得中规中矩。用字串ptr[0..len-1]进行初始化。其中注意分配空间时要加1.如下所示:
str->ptr = ALLOC_N(char,len+1);
下面的函数就用到了orig分量。
VALUE
str_new3(str)
struct RString *str;
{
NEWOBJ(str2, struct RString);
OBJSETUP(str2, C_String, T_STRING);
str2->len = str->len;
str2->ptr = str->ptr;
str2->orig = str;
return (VALUE)str2;
}
但是,并没有真正的为str2分配空间,而是让str2->orig指向已经有的str,这是什么意思?不明白。
static ID pr_str = Qnil;
VALUE
obj_as_string(obj)
VALUE obj;
{
VALUE str;
if (TYPE(obj) == T_STRING) {
return obj;
}
str = rb_funcall(obj, pr_str, 0);
if (TYPE(str) != T_STRING)
return Fkrn_to_s(obj);
return str;
}
这个函数好象是把某个对象转换成字串对象,以便于打印。
VALUE
Fstr_clone(str)
struct RString *str;
{
VALUE obj;
if (str->orig)
obj = str_new3(str->orig);
else
obj = str_new(str->ptr, str->len);
CLONESETUP(obj, str);
return obj;
}
这个函数的用法 是进行对象clone,但如果orig不空,只是浅复制。如果orig空,就真正的产生字串对象。
static VALUE
Fstr_new(class, str)
VALUE class;
struct RString *str;
{
Check_Type(str, T_STRING);
{
NEWOBJ(str2, struct RString);
OBJSETUP(str2, class, T_STRING);
str2->len = str->len;
str2->ptr = ALLOC_N(char, str->len+1);
if (str2->ptr) {
memmove(str2->ptr, str->ptr, str->len);
}
str2->ptr[str->len] = '\0';
str2->orig = Qnil;
return (VALUE)str2;
}
}
也是字串初始化的操作。但不是和最开始的一样功能吗?我想,或许在s=String.new("okok")之类操作中使用吗?不明白其使用场景。
static VALUE
Fstr_length(str)
struct RString *str;
{
return INT2FIX(str->len);
}
这个函数计算字串的长度。我记得C语言需要用库函数来能得到字串长度。ruby直接可以取字串长度。省事了。
VALUE
Fstr_plus(str1, str2)
struct RString *str1, *str2;
{
struct RString *str3;
GC_LINK;
GC_PRO3(str2, as_str(str2));
str3 = (struct RString*)str_new(0, str1->len+str2->len);
memcpy(str3->ptr, str1->ptr, str1->len);
memcpy(str3->ptr+str1->len, str2->ptr, str2->len);
str3->ptr[str3->len] = '\0';
GC_UNLINK;
return (VALUE)str3;
}
函数实现功能是:s1="abc";s2="def"
s1+s2结果是"abcdef"
matz用mem*函数,而没有用for循环。很好。
VALUE
Fstr_times(str, times)
struct RString *str;
VALUE times;
{
struct RString *str2;
int i;
times = NUM2INT(times);
str2 = (struct RString*)str_new(0, str->len*times);
for (i=0; i<times; i++) {
memmove(str2->ptr+(i*str->len), str->ptr, str->len);
}
str2->ptr[str2->len] = '\0';
return (VALUE)str2;
}
该函数实现功能是:s1="abc";
s1*3结果是"abcabcabc"
matz的每个函数都能在一屏内显示出来,很爽。
VALUE
str_substr(str, start, len)
struct RString *str;
int start, len;
{
struct RString *str2;
if (start < 0) {
start = str->len + start;
}
if (str->len <= start) {
Fail("index %d out of range [0..%d]", start, str->len-1);
}
if (len < 0) {
Fail("Negative length %d", len);
}
str2 = (struct RString*)str_new(str->ptr+start, len);
return (VALUE)str2;
}
求字串的子串。如s1="abcdefg"
s1[2,3]结果为"cde"
作者没有对len的长度进行限制。
VALUE
str_subseq(str, beg, end)
struct RString *str;
int beg, end;
{
int len;
if (beg < 0) {
beg = str->len + beg;
if (beg < 0) beg = 0;
}
if (end < 0) {
end = str->len + end;
if (end < 0) end = 0;
}
if (beg > end) {
int tmp;
if (verbose) {
Warning("start %d is bigger than end %d", beg, end);
}
tmp = beg; beg = end; end = tmp;
}
if (beg >= str->len) {
return str_new(0, 0);
}
if (str->len < end) {
end = str->len;
}
len = end - beg + 1;
if (len < 0) {
Fail("end %d too small(size %d)", end, str->len);
}
return str_substr(str, beg, len);
}
这个函数再调用 str_substr,并对len进行了处理。当ben+len超出str的末尾时,进行了处理。
void
str_modify(str)
struct RString *str;
{
if (str->orig == Qnil) return;
str->ptr = ALLOC_N(char, str->len+1);
if (str->ptr) {
memcpy(str->ptr, str->orig->ptr, str->len+1);
}
str->orig = Qnil;
}
这个函数可有些莫其妙了。你看。用orig中的字串内容恢复,orig中的内容使用过后,就变为nil,什么意思,备份只能使用一次?不懂。
我实在受不了,没有缩进代码不好看,于是在vimrc文件中进行修改。
set tabstop=8 " 1个Tab占4个空格
这下代码舒服多了。
VALUE
str_grow(str, len)
struct RString *str;
UINT len;
{
str_modify(str);
if (len > 0) {
REALLOC_N(str->ptr, char, len + 1);
str->len = len;
str->ptr[len] = '\0'; /* sentinel */
}
return (VALUE)str;
}
不过,字串的空间进行扩大,按传进来的大小加1后,再分配空间。
VALUE
str_cat(str, ptr, len)
struct RString *str;
char *ptr;
UINT len;
{
str_modify(str);
if (len > 0) {
REALLOC_N(str->ptr, char, str->len + len + 1);
if (ptr)
memmove(str->ptr + str->len, ptr, len);
str->len += len;
str->ptr[str->len] = '\0'; /* sentinel */
}
return (VALUE)str;
}
实现的功能 是:s="abc";s1="def"
s.strcat(s1)结果是"abcdef"
就是把字串s1追加到s后面。还是使用memmove复制。
static char
str_next(s)
char *s;
{
char c = *s;
/* control code */
if (c < ' ') return 0;
/* numerics */
if ('0' <= c && c < '9') (*s)++;
else if (c == '9') {
*s = '0';
return '1';
}
/* small alphabets */
else if ('a' <= c && c < 'z') (*s)++;
else if (c == 'z') {
return *s = 'a';
}
/* capital alphabets */
else if ('A' <= c && c < 'Z') (*s)++;
else if (c == 'Z') {
return *s = 'A';
}
return 0;
}
这个函数是计算字符的下一字符。如
'a'的next是'b','z'的next是'aa'
函数中有两个地方有传出的值,一个是参数,一个是函数的返回值。如
c='z';
d=str(&c);
此时d=='a',c=='a'
合起来,就是'z' 的后继为'aa'
static VALUE
Fstr_next(orig)
struct RString *orig;
{
struct RString *str, *str2;
char *sbeg, *s;
char c = -1;
GC_LINK;
GC_PRO3(str, (struct RString*)str_new(orig->ptr, orig->len));
sbeg = str->ptr; s = sbeg + str->len - 1;
while (sbeg <= s) {
if (isalnum(*s) && (c = str_next(s)) == Qnil) break;
s--;
}
if (s < sbeg && c != -1) {
GC_PRO3(str2, (struct RString*)str_new(0, str->len+1));
str2->ptr[0] = c;
memmove(str2->ptr+1, str->ptr, str->len);
obj_free(str);
str = str2;
}
GC_UNLINK;
return (VALUE)str;
}
这个函数写得很巧妙。因为我知道"z".next是"aa",一定要考虑到函数str_next的返回值
我想,要慢慢的回味。
while (sbeg <= s) {
if (isalnum(*s) && (c = str_next(s)) == Qnil) break;
s--;
}
的功能是遇到字符数字就进行递增。但c的返回值要么为0要么为其它字符,为什么后面要判断为-1呢?
if (s < sbeg && c != -1) {
GC_PRO3(str2, (struct RString*)str_new(0, str->len+1));
str2->ptr[0] = c;
memmove(str2->ptr+1, str->ptr, str->len);
obj_free(str);
str = str2;
}
上面一句:
if (s < sbeg && c != -1) {
c肯定不会和-1相等呀。
我要慢慢调试了。
当然作者的意思是先给c的初始值是-1,但只要函数str_next调用了,返回值要么是0,要么是字符。不可能是-1的,所以这个条件是可以去掉的。
static
str_hash(str)
struct RString *str;
{
int len = str->len;
unsigned char *p = (unsigned char*)str->ptr;
int key = 0;
if (ignorecase) {
while (len--) {
key = key*65599 + *p;
}
}
else {
while (len--) {
key = key*65599 + toupper(*p);
}
}
return key;
}
我曾经分析过,此处应该对p指针递增,所以这里有一个bug.
int
str_cmp(str1, str2)
struct RString *str1, *str2;
{
UINT len;
int retval;
if (ignorecase != Qnil) {
return str_cicmp(str1, str2);
}
len = min(str1->len, str2->len);
retval = memcmp(str1->ptr, str2->ptr, len);
if (retval == 0) {
return str1->ptr[len] - str2->ptr[len];
}
return retval;
}
判断两个字串是否相等。
static VALUE
Fstr_match(this, other)
struct RString *this, *other;
{
VALUE reg;
int start;
switch (TYPE(other)) {
case T_REGEXP:
return Freg_match(other, this);
case T_STRING:
reg = re_regcomp(other);
start = research(reg, this, 0, ignorecase);
if (start == -1) {
return FALSE;
}
return INT2FIX(start);
default:
Fail("type mismatch");
break;
}
}
很惭愧,几个正则表达式相关的函数没弄明白。下次再深入。
static int
str_index(str, sub, offset)
struct RString *str, *sub;
int offset;
{
char *s, *e, *p;
int len;
if (str->len - offset < sub->len) return -1;
s = str->ptr+offset;
p = sub->ptr;
len = sub->len;
e = s + str->len - len + 1;
while (s < e) {
if (*s == *(sub->ptr) && memcmp(s, p, len) == 0) {
return (s-(str->ptr));
}
s++;
}
return -1;
}
函数实现功能是:s1="abcdbc",s1.index("bc",0)结果是1,
s1.index("bc",2)结果是4,
字串函数还有许多。下次再写吧。