STL 的 rope 分析(七)

    #-rope #-STL

    _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>

  • Recent Post</i> </ul> </div>