📄 z_shstr.cpp
字号:
/*
* Copyright (c) 2003-2005 Zbchun.
* 这是一个开源软件,允许任意使用,但必须保留该版权声明。
*/
#include <stdarg.h>
#include <z_shstr.h>
#include <z_algo.h>
#include <z_useio.h>
/////////////////////////////////////////////////////////////////////////////////////
// share_str的语义探讨(2004-08-08)
//
// 共享字符串类share_str,通过对ZbcStringBuf的读共享,达到了减少拷贝(strcpy)、测长(strlen)
// 等操作次数的目的,并合理节约了资源。share_str对缓冲资源(ZbcStringBuf)的利用采取了“共享读,
// 写时拷贝(COW)”的策略。
//
// share_str采用引用计数的技术来管理ZbcStringBuf的使用。每个ZbcStringBuf维护一个引用计
// 数,来表示自身的占用状态。ZbcStringBuf的占用状态可分为三种:独占、共享、锁定。其中,
// 独占、锁定状态都表明只有一个share_str引用该资源,表述如下:
// 1.只有一个share_str对象引用缓冲资源;
// 2.同时多个share_str对象引用该资源;
// 3.某share_str对象锁定该资源,以备写入操作。
//
// share_str对象进行写入操作之前,必须先执行锁定操作,保证自身对缓冲资源的独占,保证在
// 自身对资源解锁之前,不会有其他share_str对象使用(不允许共享、写,但可以读)该资源。不允许
// 对share_str作重复锁定,后续的锁定企图将被忽略。
//
// share_str的各种语义:
//
// 一、share_str的拷贝:
//
// 如果被拷贝share_str未处于锁定状态下,则令两个share_str引用同一个ZbcStringBuf
// 同时,增加该ZbcStringBuf的引用计数;如果被拷贝share_str处于锁定状态下,则将被拷贝share_str的
// ZbcStringBuf复制一份,并对该复制品进行解锁。
//
// 二、share_str的锁定:
//
// 根据锁定前ZbcStringBuf的三种状态,必须作分别处理:
// 1.如果ZbcStringBuf处于独占状态,则该ZbcStringBuf可以直接转变到锁定状态;
// 2.如果ZbcStringBuf处于共享状态,则建立一份对该ZbcStringBuf的拷贝,将该拷贝设置为
// 锁定状态;
// 3.如果ZbcStringBuf处于锁定状态,则意味着程序员对share_str的使用有不当之处,请求被忽略。
//
// 三、share_str的锁定期语义:
//
// share_str被锁定后,处于一种不稳定,不安全状态。系统保证该share_str引用的ZbcStringBuf一直
// 有效,不被其它share_str干扰,保证ZbcStringBuf能被当成普通字符数组使用,数组有效长度为锁定长度+1,
// 但不保证该share_str的长度等有效,对share_str的操作正确性必须由用户自己加以保证,用户必须保证
// 对锁定缓冲区的操作不超过锁定范围。
//
// 四、share_str的解锁:
//
// share_str解锁的时候将重新测试字符串的实际长度,如果用户程序存在错误,导致字符串没有
// 正常结束符,则将导致一个错误报告。
//
// 五、share_str的增容:
//
// 由于share_str的连接操作相对频繁,为了避免过多的内存分配、释放操作,本实现采用了冗余
// 技术。分配ZbcStringBuf时,可以分配比实际需要大一些的缓冲,从而减少字符串连接操作时的
// 发生缓冲溢出的次数,进而减少内存分配、释放操作。
//
// 六、share_str的容器语义:(2004-08-16)
//
// 增加和其他标准容器兼容的接口,包括begin, end, push_back, pop_back, Iterator等。
//
/////////////////////////////////////////////////////////////////////////////////////
share_str::ZbcStringBuf share_str::m_nullString = {1, 0};
/////////////////////////////////////////////////////////////////////////////////////
/**
* @fn allocate
* @brief 分配bufSize长度的缓存,并将szSrc的前strLen拷贝到这个新分配缓存中
* @remark
* allocate分配函数一定能够返回一个有效的对象指针,
* 如果内存不足,则返回一个叫做m_nullString的对象指针,该对象代表空字符串
* @arg bufSize 缓冲区的长度
* @arg szSrc 需要拷贝到新缓冲区内的字符串
* @arg strLen 字符串的长度
* @return 分配得到的缓冲区
*/
share_str::ZbcStringBuf* share_str::allocate(size_t bufSize, const char* szSrc, size_t strLen)
{
ASSERT(szSrc && bufSize >= strLen);
if (bufSize == 0)
return ref_null_string();
size_t totalSize = sizeof(ZbcStringBuf)-3+bufSize; //需要的最小长度
totalSize = (totalSize+sizeof(int)-1)&~(sizeof(int)-1);//计算边界对齐后实际分配的缓冲区长度
bufSize = totalSize - sizeof(ZbcStringBuf) + 3; //得到实际可容纳字串最大长度
ZbcStringBuf* pBuf = (ZbcStringBuf* )malloc(totalSize);
if (!pBuf)
return ref_null_string();
memcpy(pBuf->m_szBuf, szSrc, strLen);
pBuf->m_szBuf[strLen] = '\0';
pBuf->m_refcnt = 1; //分配时,就有一个引用
pBuf->m_len = strLen;
pBuf->m_bufsize = bufSize;
return pBuf;
}
share_str::ZbcStringBuf* share_str::allocate(size_t bufSize)
{
if (bufSize == 0)
return NULL;
// 计算需要分配的缓冲区最小长度(进行边界对齐)
size_t totalSize = (sizeof(ZbcStringBuf)+bufSize) & ~(sizeof(int)-1);
ZbcStringBuf* pBuf = (ZbcStringBuf*)malloc(totalSize);
if (!pBuf)
return NULL;
pBuf->m_refcnt = 1; //分配时,就有一个引用
pBuf->m_bufsize = totalSize - sizeof(ZbcStringBuf) + 3;//得到实际可容纳字串最大长度
return pBuf;
}
/**
* @fn lock
* @brief 锁定缓冲区
* @arg rq_len 需要的缓冲区最小长度
* @arg pNewBuf
*/
char* share_str::lock(size_t rq_len, share_str::ZbcStringBuf** ppNewBuf)
{
ASSERT(!islock());
ASSERT(ppNewBuf);
if (rq_len < 1) //如果忽略此项检查,将导致nullString被锁定,从而在addRef中引起stack overflow错误.
rq_len = 1;
if (m_pBuf->m_refcnt == 1 && rq_len <= m_pBuf->m_bufsize){//只有一个引用缓冲的字符串,并且缓冲的长度够用
*ppNewBuf = m_pBuf;
}else if(islock()){ //缓冲已经锁定,不许再锁定一次
*ppNewBuf = NULL;
return NULL;
}else{ //须重新分配
*ppNewBuf = allocate(rq_len, c_str(), length());
if ((*ppNewBuf)->m_bufsize == 0) //分配失败
return NULL;
}
(*ppNewBuf)->m_refcnt = MAGIC_LOCK_CNT; //锁定新分配缓冲区
return (*ppNewBuf)->m_szBuf;
}
/////////////////////////////////////////////////////////////////////////////////////
share_str& share_str::operator=(const share_str& rhs)
{
if (m_pBuf != rhs.m_pBuf){ //防止自赋值,modified in the 2004-08-08
releaseRef();
m_pBuf = rhs.addRef();
}
return *this;
}
share_str& share_str::operator=(const char* str)
{
ZbcStringBuf* pNewBuf = allocate(str);
releaseRef();
m_pBuf = pNewBuf;
return *this;
}
share_str& share_str::operator=(char c)
{
share_str tmp(c);
return *this = tmp;
}
share_str& share_str::print(const char* fmt, ...)
{
char buf[1024];
va_list vl;
va_start(vl, fmt);
_vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
buf[sizeof(buf)-1] = '\0';
return *this = buf;
}
share_str share_str::print_str(const char* fmt, ...)
{
char buf[1024];
va_list vl;
va_start(vl, fmt);
_vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
buf[sizeof(buf)-1] = '\0';
return buf;
}
/**
* @brief 保留足够的字符缓冲区
* @arg bufSize 需要保留的缓冲区长度
* @return 保留空间是否成功
*/
bool share_str::reserved(size_t bufSize)
{
if (m_pBuf->m_bufsize >= bufSize) //已经够长了
return true;
ZbcStringBuf* pBuf = allocate(bufSize, c_str(), length());//分配一个足够长的缓冲
if (pBuf->m_bufsize == 0) //缓冲区分配失败
return false;
if (m_pBuf->m_refcnt == MAGIC_LOCK_CNT)
pBuf->m_refcnt = MAGIC_LOCK_CNT; //原来是锁定状态现在仍然是
releaseRef(); //释放原来的字符串缓冲
m_pBuf = pBuf;
return true;
}
//已经防止了重叠拷贝
share_str& share_str::operator+=(const char* str)
{
if (!str)
return *this;
int lht_len = length();
int rht_len = strlen(str);
int total_len = lht_len+rht_len;
ZbcStringBuf* pNewBuf;
char* p = lock(total_len, &pNewBuf);
if (p){
memcpy(p+lht_len, str, rht_len+1);
pNewBuf->m_refcnt = 1;
pNewBuf->m_len = total_len;
if (pNewBuf != m_pBuf){
releaseRef();
m_pBuf = pNewBuf;
}
}
return *this;
}
//自己加上自己,怎么办?没有问题!
//strcat(p, p)会怎么样,标准上说无定义
share_str& share_str::operator+=(const share_str& rhs)
{
int lht_len = length();
int rht_len = rhs.length();
int total_len = lht_len+rht_len;
char* p = lock(total_len);
if (p){
memcpy(p+lht_len, rhs.c_str(), rht_len+1);
unlock(total_len);
}
return *this;
}
/**
* @brief 在字符串最后添加一个字符,假如该字符是'\0',不做任何事
* @arg c 添加的字符
* @return 该字符串本身的引用
*/
share_str& share_str::operator+=(char c)
{
if (c){
size_t len = length()+1;
char* p = lock(len);
if (p){
p[len-1] = c;
p[len] = '\0';
unlock(len);
}
}
return *this;
}
/**
* @brief 锁定字符串的缓冲区
* 字符串一旦锁定,就不可共享,长度无效,必须自行计算长度,直到解锁
* 一切对该字符串的修改企图(包括再次锁定)也都将导致失败,除非进行解锁以后
* @arg rq_len 申请的缓冲长度, 如果rq_len < length(), 那将自动加长rq_len = length()
*/
char* share_str::lock(size_t rq_len)
{
ZbcStringBuf* pNewBuf;
char* pStr = lock(rq_len, &pNewBuf);
if (!pStr) //锁定失败,资源不足
return NULL;
if (pNewBuf != m_pBuf){ //锁定后,改为使用新缓冲,就必须释放原来的缓冲
releaseRef();
m_pBuf = pNewBuf;
}
return pStr;
}
/**
* @brief 设置某位置处的字符
* 如果该字符为'\0',将导致字符串被截断,长度自动调整
* @arg idx 位置索引
* @arg c 字符
*/
void share_str::set(size_t index, char c)
{
size_t slen = length();
if (index < slen){
char* p = lock();
if (p){
p[index] = c;
unlock(c == '\0' ? index : slen);
}
}
}
/**
* @brief 在字符串的某位置插入一个字符串的一部分
* @arg nPos 插入位置
* @arg strIns 插入的字符串
* @arg nInsLen 需要插入的长度
* @return 插入是否成功
*/
bool share_str::insert(size_t nPos, const char* strIns, size_t nInsLen)
{
ASSERT(nPos <= length());
if (nPos > length())
return false;
else if(!nInsLen)
return true;
int orgLen = length();
int totalLen = nInsLen+orgLen;
char* pStr = lock(totalLen);
if (!pStr)
return false;
memmove(pStr+nPos+nInsLen, pStr+nPos, orgLen-nPos+1);
memcpy(pStr+nPos, strIns, nInsLen);
unlock(totalLen);
return true;
}
/**
* @brief 在字符串的某位置开始擦除若干字符
* @arg nPos 擦除位置
* @arg nRemoveLen 需要擦除的长度
* @return 删除是否成功
*/
bool share_str::remove(size_t nPos, size_t nRemoveLen)
{
size_t slen = length();
ASSERT(nPos <= slen && nPos+nRemoveLen <= slen);
char* p = lock();
if (p){
size_t n = nPos+nRemoveLen;
memmove(p+nPos, p+n, slen-n+1);
unlock(slen-nRemoveLen);
return true;
}
return false;
}
#if 0
share_str share_str::sub(size_t nPos, size_t len) const
{//DO:2004-07-26
size_t slen = length();
if (nPos+len > slen)
len = slen-nPos;
if (nPos >= slen || len <= 0)
return "";
share_str tmp;
char* pbuf = tmp.lock(len);
if (pbuf){
strncpy(pbuf, c_str()+nPos, len);
pbuf[len] = '\0';
tmp.unlock();
}
return tmp;
}
#endif
/**
* @brief 取子串[nBegin, nEnd)
* @arg nBegin 子串的开始位置
* @arg nEnd 子串的结束位置
*/
share_str share_str::substr(size_t nBegin, size_t nEnd) const
{
ASSERT(nEnd >= nBegin);
size_t slen = length();
if (nBegin >= slen)
return share_str();
if (nEnd > slen)
nEnd = slen;
return share_str(c_str()+nBegin, c_str()+nEnd);
}
void share_str::swap(share_str& rhs)
{
::zswap(m_pBuf, rhs.m_pBuf);
}
#if 0
bool share_str::pop_back()
{
int len = length();
if (len < 1)
return false;
char* pbuf = lock();
if (pbuf){
pbuf[len-1] = '\0';
unlock(len-1);
return true;
}
return false;
}
#endif
ostream& operator<<(ostream& os, const String& str)
{
os << str.c_str();
return os;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//在%MSCDir%\Common\MSDev98\Bin\AUTOEXP.dat中增加如下一段信息,可优化调试器中所显示的share_str内容
//share_str =ref=<m_pBuf->m_refcnt,d>,str=<m_pBuf->m_szBuf,s>
//////////////////////////////////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -