redis之sds(simple dynamic string)阅读笔记3-sds的内存扩张和收缩
redis之sds(simple dynamic string)阅读笔记3-sds的内存扩张和收缩 ****************************************************************** 函数sdsMakeRoomFor负责内存扩张 /* Enlarge the free space at the end of the sds string so that the caller * is sure that after calling this function can overwrite up to addlen * bytes after the end of the string, plus one more byte for nul term. 在字符串sds的末尾扩张自由内存空间, 在调用这个函数之后调用者可以保证在字符串末尾能够覆盖新增的字节, 外加一个字节的结尾符(/0) * Note: this does not change the *length* of the sds string as returned * by sdslen(), but only the free buffer space we have. */ 这个调用不影响函数sdslen()放回的字符串sds的长度,只是改变了(增加)原来的自由内存空间 ****************************************************************** sds sdsMakeRoomFor(sds s, size_t addlen) { void *sh, *newsh; //定义两个指向结构体的指针 size_t avail = sdsavail(s); //获取字符串剩余的自由内存空间 size_t len, newlen; //定义字符串使用原来的长度 和 新长度变量 char type, oldtype = s[-1] & SDS_TYPE_MASK; //定义字符串类型 和 原来的类型,并且给原类型赋值 //s[-1]是flags的值 和 SDS_TYPE_MASK(7) 相与,获取的就是字符串的类型 int hdrlen; //结构体长度 /* Return ASAP if there is enough space left. */ if (avail >= addlen) return s; //如果剩余自由空间大于需要新增的空间,就不用新分配自由空间了,直接返回 len = sdslen(s); //获取原字符串长度 sh = (char*)s-sdsHdrSize(oldtype); //获取指向原来结构体的指针 newlen = (len+addlen); // 新的字符串长度 等于原字符串长度 加上 新增内容的长度 if (newlen < SDS_MAX_PREALLOC) newlen *= 2; //SDS_MAX_PREALLOC=1024*1024=1M BYTES,如果新字符串长度小于1M,那么扩张的长度为新字符串的两倍 else newlen += SDS_MAX_PREALLOC; //否则,即如果新字符串长度大于等于1M,那么扩张的长度为 新字符串的长度+SDS_MAX_PREALLOC type = sdsReqType(newlen); //获取新字符串的所需类型 /* Don't use type 5: the user is appending to the string and type 5 is * not able to remember empty space, so sdsMakeRoomFor() must be called * at every appending operation. */ //不要使用type5的类型,因为type5的类型不能记忆字符串自由空间的大小, if (type == SDS_TYPE_5) type = SDS_TYPE_8; //至少使用SDS_TYPE_8 hdrlen = sdsHdrSize(type);//后去对应类型结构体的大小 if (oldtype==type) { //如果原类型和新类型一致(意味着结构体的大小不变) newsh = s_realloc(sh, hdrlen+newlen+1); //所以可以在原结构体基础上扩建自由内存空间 if (newsh == NULL) return NULL; // 获取不到自由内存空间,返回空 s = (char*)newsh+hdrlen; //获取到自由内存空间,获取新字符串的首地址 } else { /* Since the header size changes, need to move the string forward, * and can't use realloc */ //因为结构体的大小变了,不能再使用realloc末尾添加函数了,需要重新整个结构体搬迁 newsh = s_malloc(hdrlen+newlen+1); //为整个结构体加上新字符串外加1个字节的结尾符分配自由内存空间 if (newsh == NULL) return NULL; memcpy((char*)newsh+hdrlen, s, len+1);//分配成功,那么将原字符串先拷贝过来 s_free(sh); //释放原来的结构体 s = (char*)newsh+hdrlen; //获取新字符串的首地址=新分配空间的首地址 + 结构体的大小 s[-1] = type; // 赋值新的类型 sdssetlen(s, len); //设置新的字符串长度(这个函数只是获取新增空间,并且没有真的将新的字符拷贝到这里,所以长度保持原样) } sdssetalloc(s, newlen);//设置新结构体已分配字符串内存长度 return s; } ****************************************************************** 函数sdsavail获取字符串可用(自由)空间长度 static inline size_t sdsavail(const sds s) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { // SDS_TYPE_MASK= 7 获取字符串类型 case SDS_TYPE_5: { return 0; } case SDS_TYPE_8: { SDS_HDR_VAR(8,s); //获取结构体指针 return sh->alloc - sh->len; //获取结构体可用空间长度 } case SDS_TYPE_16: { SDS_HDR_VAR(16,s); return sh->alloc - sh->len; } case SDS_TYPE_32: { SDS_HDR_VAR(32,s); return sh->alloc - sh->len; } case SDS_TYPE_64: { SDS_HDR_VAR(64,s); return sh->alloc - sh->len; } } return 0; } ****************************************************************** 函数sdssetalloc设置结构体已分配内存空间 static inline void sdssetalloc(sds s, size_t newlen) { unsigned char flags = s[-1]; //获取字符串类型 switch(flags&SDS_TYPE_MASK) { // SDS_TYPE_MASK= 7 获取字符串类型 case SDS_TYPE_5: /* Nothing to do, this type has no total allocation info. */ break; case SDS_TYPE_8: SDS_HDR(8,s)->alloc = newlen; // 赋值新分配内存空间给结构体成员alloc break; case SDS_TYPE_16: SDS_HDR(16,s)->alloc = newlen; break; case SDS_TYPE_32: SDS_HDR(32,s)->alloc = newlen; break; case SDS_TYPE_64: SDS_HDR(64,s)->alloc = newlen; break; } } ****************************************************************** 函数sdsRemoveFreeSpace负责内存收缩 ****************************************************************** /* Reallocate the sds string so that it has no free space at the end. The * contained string remains not altered, but next concatenation operations * will require a reallocation. 重新分配内存空间,使得在字符串sds结尾没有多余的内存空间, 但是字符串的内容保持不变,这一些列操作可能会导致重新分配内存 * After the call, the passed sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ 经过这个函数的调用,传入的字符串sds被释放,所有原字符串的引用将由调用返回的新指向字符串代替 sds sdsRemoveFreeSpace(sds s) { void *sh, *newsh; //定义指向新老结构体的指针 char type, oldtype = s[-1] & SDS_TYPE_MASK; //定义新老结构体的类型,对老结构体类型赋值 int hdrlen, oldhdrlen = sdsHdrSize(oldtype); //定义新老结构体的长度,对老结构体长度赋值 size_t len = sdslen(s); //获取老字符串长度 size_t avail = sdsavail(s);//获取老字符串可用空间 sh = (char*)s-oldhdrlen; //获取指向老结构体的指针 /* Return ASAP if there is no space left. */ if (avail == 0) return s; //如果可用空间为0,就返回原字符串 /* Check what would be the minimum SDS header that is just good enough to * fit this string. */ //使用满足字符串长度的最小结构体类型 type = sdsReqType(len); //获取新结构体类型 hdrlen = sdsHdrSize(type);//获取新结构体类型大小 /* If the type is the same, or at least a large enough type is still * required, we just realloc(), letting the allocator to do the copy * only if really needed. Otherwise if the change is huge, we manually * reallocate the string to use the different header type. */ if (oldtype==type || type > SDS_TYPE_8) { //如果新老结构体类型一致 或者 所需结构体类型大于SDS_TYPE_8(255) newsh = s_realloc(sh, oldhdrlen+len+1); //使用realloc重新分配 老结构体长度+字符串长度+1 大小的内存 //realloc 函数具备迁移原内存内容的功能 if (newsh == NULL) return NULL; s = (char*)newsh+oldhdrlen; //重新获取字符串首地址(因为realloc函数可能会改变内存地址) } else { newsh = s_malloc(hdrlen+len+1); //分配 新结构体长度+字符串长度+1 长度的内存 if (newsh == NULL) return NULL; memcpy((char*)newsh+hdrlen, s, len+1); //将原字符串拷贝到新地址 s_free(sh);//释放来的结构体内存 s = (char*)newsh+hdrlen;//重新获取字符串首地址 s[-1] = type;//赋值新结构体类型 sdssetlen(s, len); //设置新结构体长度 } sdssetalloc(s, len);//设置新结构体已分配字符串内存长度 return s; } ****************************************************************** 函数sdsAllocSize返回所有分配的内存大小 结构体大小+已用字符串内存大小+未用字符串内存大小+结尾符号 /* Return the total size of the allocation of the specified sds string, * including: * 1) The sds header before the pointer. * 2) The string. * 3) The free buffer at the end if any. * 4) The implicit null term. */ size_t sdsAllocSize(sds s) { size_t alloc = sdsalloc(s); return sdsHdrSize(s[-1])+alloc+1; } ****************************************************************** 函数sdsAllocPtr返回指向结构体的指针 /* Return the pointer of the actual SDS allocation (normally SDS strings * are referenced by the start of the string buffer). */ void *sdsAllocPtr(sds s) { return (void*) (s-sdsHdrSize(s[-1])); } ******************************************************************