_S_flatten 函数用来将用 __r 表示的 rope 中的元素拷贝到 __buffer 中,即获得 rope 的元素序列。其中函数的返回值为元素拷贝到 __buffer 中后的截止地址。
如果当前 __r 节点的类型为 _S_concat ,则先将 __r 左子树上的元素拷贝到 __buffer 开始的地方,然后再将右子树上的元素拷贝到左子树的后面。如果 __r 节点的类型为 _S_leaf ,则直接将 _r 中 _M_data 的内容拷贝到 __buffer 中,并且返回结束地址。
如果当前 __r 节点的类型为 _S_function 或者 _S_substringfn。则直接用 __r 的 _M_fn 函数生成 __r 节点中的元素,并将生成的元素放置到 __buffer 开始的位置。返回结束地址 __buffer + __f->_M_size 。
template <class _CharT, class _Alloc>
_CharT*
rope<_CharT,_Alloc>::_S_flatten(_RopeRep* __r, _CharT* __buffer)
{
if (0 == __r) return __buffer;
switch(__r->_M_tag) {
case _RopeRep::_S_concat:
{
_RopeConcatenation* __c = (_RopeConcatenation*)__r;
_RopeRep* __left = __c->_M_left;
_RopeRep* __right = __c->_M_right;
_CharT* __rest = _S_flatten(__left, __buffer);
return _S_flatten(__right, __rest);
}
case _RopeRep::_S_leaf:
{
_RopeLeaf* __l = (_RopeLeaf*)__r;
return copy_n(__l->_M_data, __l->_M_size, __buffer).second;
}
case _RopeRep::_S_function:
case _RopeRep::_S_substringfn:
// We dont yet do anything with substring nodes.
// This needs to be fixed before ropefiles will work well.
{
_RopeFunction* __f = (_RopeFunction*)__r;
(*(__f->_M_fn))(0, __f->_M_size, __buffer);
return __buffer + __f->_M_size;
}
default:
__stl_assert(false);
/*NOTREACHED*/
return 0;
}
}
####类模板 _Rope_flatten_char_consumer####
类模板 _Rope_flatten_char_consumer 继承自 _Rope_char_consumer<_CharT> 。其内部定义了一个成员变量 _M_buf_ptr。operator 函数中定义的操作是将指定位置 __leaf 开始的长度为 __len 的元素拷贝到 _M_buf_ptr 开始的位置。同时修改 _M_buf_ptr 的值以便后续的元素拷贝到当前元素的后面。
template<class _CharT>
class _Rope_flatten_char_consumer : public _Rope_char_consumer<_CharT> {
private:
_CharT* _M_buf_ptr;
public:
_Rope_flatten_char_consumer(_CharT* __buffer) {
_M_buf_ptr = __buffer;
};
~_Rope_flatten_char_consumer() {}
bool operator() (const _CharT* __leaf, size_t __n) {
uninitialized_copy_n(__leaf, __n, _M_buf_ptr);
_M_buf_ptr += __n;
return true;
}
};
_S_flatten 将 __r 表示的 rope 中从 __start 位置开始长度为 __len 的元素序列拷贝到 __buffer 中。函数中首先声明了一个 _Rope_flatten_char_consumer<_CharT> 的实例 __c,__c 用 __buffer 进行初始化,使得 __c 中定义的操作就是将指定位置的元素复制到 __buffer 开始的地方。然后调用 _S_apply_to_pieces(__c, __r, __start, __start + __len) 使得将 __r 表示的 rope 中从 __start 开始到 __start + __len 之间的元素都应该 __c.operator 中定义的操作(即复制到__c._M_buf_ptr(初始时为 __buffer) 开始的位置)。
template <class _CharT, class _Alloc>
_CharT*
rope<_CharT,_Alloc>::_S_flatten(_RopeRep* __r,
size_t __start, size_t __len,
_CharT* __buffer)
{
_Rope_flatten_char_consumer<_CharT> __c(__buffer);
_S_apply_to_pieces(__c, __r, __start, __start + __len);
return(__buffer + __len);
}
rope 中定义了一个长度为 _S_max_rope_depth + 1 的常量数组 _S_min_len。其中 _S_min_len[i] 的值为第 i + 2 个斐波那契数。即 _S_min_len[0] = 1, _S_min_len[1] = 2, 依次类推。
static const unsigned long
_S_min_len[_RopeRep::_S_max_rope_depth + 1];
函数 _S_is_balanced 用来判断以给定节点为根的树是否平衡,判定当 __r->_M_size>= _S_min_len[__r->_M_depth] 时则认为是平衡的,即深度为 _M_depth 的树至少要有 _S_min_len[_M_depth] 个节点才认为是平衡的(这也是 avl 平衡树的要求)。
static bool _S_is_balanced(_RopeRep* __r)
{ return (__r->_M_size >= _S_min_len[__r->_M_depth]); }
函数 _S_is_almost_balanced 用来判断以给定节点为根的树是否是近似平衡的,判断当 _M_depth == 0 或者 __r->_M_size >= _S_min_len[_M_depth - 1] 时认为以 __r 为根的树是近似平衡的。近似平衡的条件弱于平衡的条件。
static bool _S_is_almost_balanced(_RopeRep* __r)
{ return (__r->_M_depth == 0 ||
__r->_M_size >= _S_min_len[__r->_M_depth - 1]); }
函数 _S_is_roughly_balanced 判断以给定节点为根的树是否是大致平衡的,判定当 _M_depth <= 1 或者 __r->_M_size >= _S_min_len[__r->_M_depth - 2] 时认为以 __r 为根的树是大致平衡的。
static bool _S_is_roughly_balanced(_RopeRep* __r)
{ return (__r->_M_depth <= 1 ||
__r->_M_size >= _S_min_len[__r->_M_depth - 2]); }
函数 _S_concat_and_set_balanced 调用 _S_concat 将 __left 为根的树和 __right 为根的树拼接成一棵树,并将根节点存放在 __result 中,判断以 result 为根的树是否是平衡的,如果是,将 __result->_M_is_balanced 置为 true。返回 __result 。
static _RopeRep* _S_concat_and_set_balanced(_RopeRep* __left,
_RopeRep* __right)
{
_RopeRep* __result = _S_concat(__left, __right);
if (_S_is_balanced(__result)) __result->_M_is_balanced = true;
return __result;
}
函数 _S_add_leaf_to_forest 用来一个 __r 为根节点的树添加到 __forsest 中,其中 __forest 可以看成是一个指针数组,其每一个元素都是一个 _RopeRep 类型的指针(实际调用是 __forest 就是一个指针数组)。
函数被调用之前有个前提,第一个是 __r 为根的树是一棵平衡树,即满足 _S_is_balanced(__r) 。对于 __forest 中的任何元素 __forest[__i],__forest[__i] 要么为空,要么是一棵近似平衡的树(也可能是平衡的),即满足 _S_is_almost_balanced(__forest[__i])。
同时对于 __forest[__i] 中的元素,如果其不为空,则 __forest[__i]->_M_size >= _S_min_len[__i],则 __forest[__i] 的深度不超过 __i + 1。
同时对与 __forest 中的不为空的元素顺序也有规范,__forest 中每个不为空的元素都代表了一棵子树。当前函数 _S_add_leaf_to_forest 只是一个子程序,最后所有 __forest 中不为空的元素要全部合并成一棵树。
合并的过程中从 __forest[0] 开始,前面的子树合并之后应该在后面的子树的右边,即如果 __forest[0] 不为空,那么合并 __forest 中的所有子树之后 __forest[0] 应该处在最右边。而将 __r 为根的子树插入到 __forest 中后,要保证合并所有 __forest 中的元素之后,__r 为根的子树处在整棵树的最右边。
即插入后如果以 __r 为根的子树处在 __forest[i] 为根的树上 (可能 __r == __forest[i] ,也可能 __r 为 __forest[i] 的子孙节点,如果 __r 为 __forest[i] 的子孙节点,那么 __r 应该在以 __forest[i] 为根的树的最右边)。那么 __forsest[0…i - 1] 应该都为空,否则 __r 为根的子树在合并了所有节点之后就不在最右边了。
函数首先声明了一个两个 _RopeRep 类型的指针,分别为 __insertee 和 __too_tiny,其中 __too_tiny 初始化为空指针 。令 __s = __r->_M_size 。
for 循环中当 __s < _S_min_len[__i + 1] 时结束,当 __s < _S_min_len[i + 1] 时,因为 __r 为平衡树,而且 __s >= _S_min[i] (否则当循环到 __i - 1 时就应退出了)。则 __r 的深度应为 __i。
在循环结束之前 __forest[0…__i - 1] 之间的子树都被合并到了 __too_tiny 中。因为对于 0 <= __j <= __i - 1,如果 __forest[__j]不为空,则以 __forest[__j] 为根的树都是近似平衡树,且 __forest[j] == _M_min_len[__j],__forest[__j] 的深度不超过 __j + 1。
__too_tiny 合并了 __forest[0…i - 1] 中的节点,合并过程中都是将 __too_tiny 作为右子节点。__too_tiny 中的节点深度最多为 __i + 1,这可以用归纳法进行证明,假设 __f 是从 0 开始 __forest[__f] 不为 0 的最小 __f 。则__too_tiny 合并[0…__f] 中的节点后 __too_tiny 的深度肯定不超过 __f + 2(实际不超过 __f + 1,为 __forest[__f] 的深度)。现在假定 __too_tiny 合并了 forest[0…j - 2] 中的元素深度不超过 __j,因为 __forest[__j - 1] 的深度不超过 __j,所以 __too_tiny 合并和 __forest[0…j - 1] 中的元素之后深度肯定不超过 __j + 1。所以 __too_tiny 合并了 __forest[0…i - 1] 中元素之后深度不超过 __i + 1。
跳出循环之后,__too_tiny 的深度不超过 __i + 1。然后将深度为 __i 的平衡树 __r 和 __too_tiny 进行合并得到 __insertee。可以肯定 __insertee 是一棵近似平衡的树。
分两种情况分析,第一种当 __forest[__i - 1] 为空时,__too_tiny 合并了 __forest[0…i - 1] 之后的深度应该不超过 __i(和合并了 __forest[0…i - 2] 是一样的)。两棵深度不超过 __i 的子树合并,得到的深度为 __i + 1。且同时合并得到的树包含的元素个数大于 _S_min_len[i] ,因为 __r 为根的树中的元素个数已经大于或者等于 _S_min_len[i] 了。
第二种情况当 __forest[i - 1] 不为空时,__too_tiny 的深度不低于 __i + 1。则合并了 __too_tiny 和 __r 之后得到的树的深度不超过 __i + 2。但由于 __forest[__i - 1] 不为空,则 __too_tiny->_M_size >= _S_min_len[__i - 1],而 __r->_M_size >= _S_min_len[i]。则根据斐波那契数的性质,合并后得到的树包含的元素个数应大于或者等于 _S_min_len[__i + 1]。所以得到的树还是近似平衡的。
对于第二个 for 循环,对于当前 __i,循环过程中的循环不变式是 insertee 的深度要么为 __i,要么为 __i + 1 要么为 __i + 2。首先进入条件时,insertee 是近似平衡树(也可能平衡),条件得以满足(初始时 __insertee 不可能为 __i)。如果 __insertee 为 __i (不可能是初始时进入循环),则 __insertee 肯定为平衡树,否则前面 __i - 1 时已经退出。此时如果 __forest[__i] 为空,则接下来会退出循环,如果 __forest[__i] 不为空,则得到一个深度为 __i + 1 的平衡树,++i 后仍然能够满足。
如果 __insertee 的深度为 __i + 1,如果 __insertee 不平衡,__forest[i] 非空,则得到一个深度为 __i + 2 的近似平衡树。++i 后仍然满足,若 __forest[i] 为空,则会退出循环。如果 __inertee 为深度为 __i + 1 且是平衡树,若 __forset[__i] 非空,得到一棵深度为 __i + 2 的平衡树,++i 后仍然满足不变式。若 __forest[__i] 为空,++i 后仍然满足不变式。
如果 __insertee 的深度为 __i + 2,且不平衡,若 __forest[__i] 非空,则得到一棵深度为 __i + 3 的近似平衡树,++i 后循环不变式仍然得到满足,若 __forest[__i] 为空,++i 后循环不变式仍然得到满足。因此第二个 for 循环退出是能保证赋值给 __forest[i] 的 __insertee 是一棵近似平衡树。
所以 _S_add_leaf_to_forest 能保证初始时以 __forest 中的所有不为空的元素为根的树都是近似平衡树,结束时所有以 __forest 中不为空的元素为根的树都是近似平衡树,且 __forest[0…__i - 1] 为空。
template <class _CharT, class _Alloc>
void
rope<_CharT,_Alloc>::_S_add_leaf_to_forest(_RopeRep* __r, _RopeRep** __forest)
{
_RopeRep* __insertee; // included in refcount
_RopeRep* __too_tiny = 0; // included in refcount
int __i; // forest[0..__i-1] is empty
size_t __s = __r->_M_size;
for (__i = 0; __s >= _S_min_len[__i+1]/* not this bucket */; ++__i) {
//the biggest tree in forest probably be combined is __forst[__i - 1],
//when __s >= _S_min_len[__i] and __s < _s_min_len[i + 1]. the loop is over.
//the __forest[i] has no chance to be combined. so the depth of __too_tiny
//won't be larger than __i. because the depth of __r is __i at most(it's balanced).
//the depth of Insertee won't be larger than __i + 1.
if (0 != __forest[__i]) {
__too_tiny = _S_concat_and_set_balanced(__forest[__i], __too_tiny);
/// __forest[__i]->_M_unref_nonnil();
__forest[__i] = 0;
}
}
{
__insertee = _S_concat_and_set_balanced(__too_tiny, __r);
}
// Too_tiny dead, and no longer included in refcount.
// Insertee is live and included.
__stl_assert(_S_is_almost_balanced(__insertee));
__stl_assert(__insertee->_M_depth <= __r->_M_depth + 1);
for (;; ++__i) {
if (0 != __forest[__i]) {
__insertee = _S_concat_and_set_balanced(__forest[__i], __insertee);
///__forest[__i]->_M_unref_nonnil();
__forest[__i] = 0;
__stl_assert(_S_is_almost_balanced(__insertee));
}
__stl_assert(_S_min_len[__i] <= __insertee->_M_size);
__stl_assert(__forest[__i] == 0);
if (__i == _RopeRep::_S_max_rope_depth ||
__insertee->_M_size < _S_min_len[__i+1]) {
__forest[__i] = __insertee;
// refcount is OK since __insertee is now dead.
return;
}
}
}
函数将 __r 插入到 __forest 中。如果 __r 为平衡树,则直接将 __r 插入到 __forest 中,并且插入前和插入后以 __forest 中的非空元素为根的树是近似平衡树,如果 __r 不为平衡树,则先将左子树插入到 __forest 再将 __r 的右子树插入到 __forest(插入的顺序不能颠倒,否则最后合并是元素的顺序就会颠倒)。
template <class _CharT, class _Alloc>
void
rope<_CharT,_Alloc>::_S_add_to_forest(_RopeRep* __r, _RopeRep** __forest)
{
if (__r->_M_is_balanced) {
_S_add_leaf_to_forest(__r, __forest);
return;
}
__stl_assert(__r->_M_tag == _RopeRep::_S_concat);
{
_RopeConcatenation* __c = (_RopeConcatenation*)__r;
_S_add_to_forest(__c->_M_left, __forest);
_S_add_to_forest(__c->_M_right, __forest);
}
}
函数 _S_balance 用来调整以给定节点 __r 为根的树,_S_balance 能保证调整后的树至少是满足 _S_roughly_balanced 的。
函数声明一个指针数组 __forest ,并将其中的所有元素都初始化为 0 。然后调用 _S_add_to_forest(__r, __forest) 将 __r 中的子树插入到 __forest 中,使得函数退出之后,以__forest 中的每个非空元素为根的树都是近似平衡的。而且将 __forest 中的元素从左往右进行合并(合并时应该使 __forest 中前面的元素处在合并后的树右边,后面的元素处在合并后的树的左边) 能还原出 __r 中的所有元素。调用一个 for 循环对 __forest 中的所有元素进行合并,每次合并时,将新加入的节点作为左子节点。最后合并的结果保存在 __result 中。但如果 __result_depth 超出了 _S_max_rope_depth 则抛出一个异常。
template <class _CharT, class _Alloc>
rope<_CharT,_Alloc>::_RopeRep*
rope<_CharT,_Alloc>::_S_balance(_RopeRep* __r)
{
_RopeRep* __forest[_RopeRep::_S_max_rope_depth + 1];
_RopeRep* __result = 0;
int __i;
// Invariant:
// The concatenation of forest in descending order is equal to __r.
// __forest[__i]._M_size >= _S_min_len[__i]
// __forest[__i]._M_depth = __i
// References from forest are included in refcount.
for (__i = 0; __i <= _RopeRep::_S_max_rope_depth; ++__i)
__forest[__i] = 0;
__STL_TRY {
_S_add_to_forest(__r, __forest);
for (__i = 0; __i <= _RopeRep::_S_max_rope_depth; ++__i)
if (0 != __forest[__i]) {
__result = _S_concat(__forest[__i], __result);
__forest[__i]->_M_unref_nonnil();
# if !defined(__GC) && defined(__STL_USE_EXCEPTIONS)
__forest[__i] = 0;
# endif
}
}
__STL_UNWIND(for(__i = 0; __i <= _RopeRep::_S_max_rope_depth; __i++)
_S_unref(__forest[__i]))
if (__result->_M_depth > _RopeRep::_S_max_rope_depth) {
# ifdef __STL_USE_EXCEPTIONS
__STL_THROW(length_error("rope too long"));
# else
abort();
# endif
}
return(__result);
}
函数 _S_dump 用来输出 __r 表示的整个 rope 的信息。其中当前函数要求 _CharT 用 char 来进行实例化。
如果 __r 节点为 _S_concat 类型,则将当前节点的类型,地址,深度,大小,是否平衡的信息输出,并递归的对左子节点和右子节点分别调用 _S_dump 函数,同时将用于缩进的实参加 2 。
如果当前节点不为 _S_concat 类型,即为 rope 的叶节点(_S_leaf, _S_function, _S_substring 三种类型)。首先将节点的类型,地址,深度,大小信息进行输出。如果 _Rope 中的元素类型为基本字符类型(char 或者 wchar_t) 则获取该节点中的前 40 个字符,如果节点中字符个数小于 40 则全部取出,然后输出获取到的内容。
template <class _CharT, class _Alloc>
void
rope<_CharT,_Alloc>::_S_dump(_RopeRep* __r, int __indent)
{
for (int __i = 0; __i < __indent; __i++) putchar(' ');
if (0 == __r) {
printf("NULL\n"); return;
}
if (_RopeRep::_S_concat == __r->_M_tag) {
_RopeConcatenation* __c = (_RopeConcatenation*)__r;
_RopeRep* __left = __c->_M_left;
_RopeRep* __right = __c->_M_right;
printf("Concatenation %p (depth = %d, len = %ld, %s balanced)\n",
__r, __r->_M_depth, __r->_M_size, __r->_M_is_balanced? "" : "not");
_S_dump(__left, __indent + 2);
_S_dump(__right, __indent + 2);
return;
} else {
char* __kind;
switch (__r->_M_tag) {
case _RopeRep::_S_leaf:
__kind = "Leaf";
break;
case _RopeRep::_S_function:
__kind = "Function";
break;
case _RopeRep::_S_substringfn:
__kind = "Function representing substring";
break;
default:
__kind = "(corrupted kind field!)";
}
printf("%s %p (depth = %d, len = %ld) ",
__kind, __r, __r->_M_depth, __r->_M_size);
if (_S_is_one_byte_char_type((_CharT*)0)) {
const int __max_len = 40;
_Self_destruct_ptr __prefix(_S_substring(__r, 0, __max_len));
_CharT __buffer[__max_len + 1];
bool __too_big = __r->_M_size > __prefix->_M_size;
_S_flatten(__prefix, __buffer);
__buffer[__prefix->_M_size] = _S_eos((_CharT*)0);
printf("%s%s\n",
(char*)__buffer, __too_big? "...\n" : "\n");
} else {
printf("\n");
}
}
}
rope 中定义了大量的函数,如构造函数,append 函数,insert 函数,erase 函数,replace 函数 copy 函数,find 函数,c_str 函数,substr 函数,operator[] 函数等等,这些函数的实现都是通过调用之前定义的函数进行实现,这里不再详述了。
STL 的 rope 分析(一)</br> STL 的 rope 分析(二)</br> STL 的 rope 分析(三)</br> STL 的 rope 分析(四)</br> STL 的 rope 分析(五)</br> STL 的 rope 分析(六)</br> STL 的 rope 分析(七)</br>