⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 z_shstr.cpp

📁 一个跨平台的共享字符串类实现
💻 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 + -