STL 的 rope 分析(五)

#-rope #-STL

####类模板 rope#### 类 rope 继承自 _Rope_base<_CharT, _Alloc> 类。

template <class _CharT, class _Alloc> class rope : public _Rope_base<_CharT,_Alloc> {

其中定义了一系列的成员类型。

    typedef _CharT value_type;
    typedef ptrdiff_t difference_type;
    typedef size_t size_type;
    typedef _CharT const_reference;
    typedef const _CharT* const_pointer;
    typedef _Rope_iterator<_CharT,_Alloc> iterator;
    typedef _Rope_const_iterator<_CharT,_Alloc> const_iterator;
    typedef _Rope_char_ref_proxy<_CharT,_Alloc> reference;
    typedef _Rope_char_ptr_proxy<_CharT,_Alloc> pointer;
    typedef _Rope_base<_CharT,_Alloc> _Base;
    typedef typename _Base::allocator_type allocator_type;
    typedef __GC_CONST _CharT* _Cstrptr;
    typedef _Rope_RopeRep<_CharT,_Alloc> _RopeRep;
    typedef _Rope_RopeConcatenation<_CharT,_Alloc> _RopeConcatenation;
    typedef _Rope_RopeLeaf<_CharT,_Alloc> _RopeLeaf;
    typedef _Rope_RopeFunction<_CharT,_Alloc> _RopeFunction;
    typedef _Rope_RopeSubstring<_CharT,_Alloc> _RopeSubstring;

定义了一个成员变量 _S_empty_c_str[1],_S_empty_c_str 用来表示当前 rope 中是否没有元素。但 _S_empty_c_str[0] = 0 时,表示 rope 中没有元素存在。同时定义一个枚举变量,_S_copy_max 作为一个常量值使用。

    static _CharT _S_empty_c_str[1];
    enum { _S_copy_max = 23 };

函数 _S_is0 用来判断给定元素 __c 是否是结束符。

    static bool _S_is0(_CharT __c) { return __c == _S_eos((_CharT*)0); }

函数 _S_fetch 用来检索 __r 表示的 rope 中在指定位置 __i 上的元素。首先检测 __r 的 _M_c_string 是否为空,如果不为空,则说明根节点是 _S_leaf 类型的节点,_M_c_string 和 _M_data 指向同一个位置。直接返回 _M_c_string[i] 即可。

如果 _M_c_string 为空,则从根节点开始往下进行搜索,如果 __r 是 _S_concat 类型的节点,令 __left 为 __r 的左子节点,令 __left_len = __left->_M_size 。如果 i >= __left_len 说明以 __r 为根的树中,第 __i 个元素在其右子树上,令 __i -= __left_len, __r 更新为其右子节点。否则如果 i < __left_len ,则说明第 __i 个节点在其左子节点上,更新 __r 为其左子节点。

如果节点类型为 _S_leaf ,则直接返回 _M_data[__i]。如果节点类型为 _S_function 或者 _S_substringfn ,声明一个 _CharT 类型的变量 __result ,调用 (*_M_fn)(__i, 1, &result) 将当前节点的第 __i 个 个元素生成放置但 __result 所在的地址上。然后将 __result 作为返回值返回。

函数没有对位置 __i 的有效性进行检测。

template <class _CharT, class _Alloc>
_CharT
rope<_CharT,_Alloc>::_S_fetch(_RopeRep* __r, size_type __i)
{
    __GC_CONST _CharT* __cstr = __r->_M_c_string;

    __stl_assert(__i < __r->_M_size);
    if (0 != __cstr) return __cstr[__i]; 
    for(;;) {
      switch(__r->_M_tag) {
	case _RopeRep::_S_concat:
	    {
		_RopeConcatenation* __c = (_RopeConcatenation*)__r;
		_RopeRep* __left = __c->_M_left;
		size_t __left_len = __left->_M_size;

		if (__i >= __left_len) {
		    __i -= __left_len;
		    __r = __c->_M_right;
		} else {
		    __r = __left;
		}
	    }
	    break;
	case _RopeRep::_S_leaf:
	    {
		_RopeLeaf* __l = (_RopeLeaf*)__r;
		return __l->_M_data[__i];
	    }
	case _RopeRep::_S_function:
	case _RopeRep::_S_substringfn:
	    {
		_RopeFunction* __f = (_RopeFunction*)__r;
		_CharT __result;

		(*(__f->_M_fn))(__i, 1, &__result);
		return __result;
	    }
      }
    }
}

函数 _S_apply_to_pieces 将 __r 表示的 rope 中从 __begin 到 __end 之间的元素都应用 __c 中 operator() 函数所定义的操作。__char_consumer 是一个抽象类,具体的操作可以在其继承类的 operator() 函数中进行定义,然后将其继承类的实例作为当前函数的实参,因为基类的引用是可以引用继承类的。

如果当前 __r 节点是 _M_concat 类型,令 __left 为其左子节点,令 __left_len = __left->_M_size 。则如果 __begin < __left_len,则说明其左子树上的一部分节点包括在 __begin 和 __end 之间,需要应用 __c 中定义的操作,调用 _S_apply_to_pieces(__c, __left, __begin, min(__left_len, __end))。

如果 __end > __left_len 则说明其右子树上的一部分节点包含在 __begin 和 __end 之间(__begin < __left_len 和 __end > __left_len 二者不互斥)。令 __right 为其右子节点,调用 _S_apply_to_pieces(__c, __right, max(left_len, __begin) - __left_len, __end - __left_len)。

如果当前 __r 节点为 _M_leaf 类型的节点,则直接对 __r 节点的数据域中 __begin 到 __end 之间的元素应用 __c 中的操作,即调用 __c((_RopeLeaf*)__r->_M_data + __begin, __end - __begin)。c.operator() 函数的第二个形参表示的是元素序列的长度。

如果当前 __r 节点的类型为 _S_function 或者 _S_substringfn,令 __len 为 __begin 到 __end 之间的长度,首先申请足够容纳 __len 个元素的空间,并将申请的空间的首地址存储在 __buffer 中,然后调用 __r 的 _M_fn,将当前节点中从 __begin 位置开始长度为 __len 的元素序列都读到 __buffer 中。再对 __buffer 中的元素应用 __c.operator() 中定义的操作。然后将 __buffer 中的空间进行回收。

template <class _CharT, class _Alloc>
bool rope<_CharT, _Alloc>::_S_apply_to_pieces(
				_Rope_char_consumer<_CharT>& __c,
				const _RopeRep* __r,
				size_t __begin, size_t __end)
{
    if (0 == __r) return true;
    switch(__r->_M_tag) {
	case _RopeRep::_S_concat:
	    {
		_RopeConcatenation* __conc = (_RopeConcatenation*)__r;
		_RopeRep* __left =  __conc->_M_left;
		size_t __left_len = __left->_M_size;
		if (__begin < __left_len) {
		    size_t __left_end = min(__left_len, __end);
		    if (!_S_apply_to_pieces(__c, __left, __begin, __left_end))
			return false;
		}
		if (__end > __left_len) {
		    _RopeRep* __right =  __conc->_M_right;
		    size_t __right_start = max(__left_len, __begin);
		    if (!_S_apply_to_pieces(__c, __right,
					 __right_start - __left_len,
					 __end - __left_len)) {
			return false;
		    }
		}
	    }
	    return true;
	case _RopeRep::_S_leaf:
	    {
		_RopeLeaf* __l = (_RopeLeaf*)__r;
		return __c(__l->_M_data + __begin, __end - __begin);
	    }
	case _RopeRep::_S_function:
	case _RopeRep::_S_substringfn:
	    {
		_RopeFunction* __f = (_RopeFunction*)__r;
		size_t __len = __end - __begin;
		bool __result;
		_CharT* __buffer =
		  (_CharT*)alloc::allocate(__len * sizeof(_CharT));
		__STL_TRY {
		  (*(__f->_M_fn))(__begin, __len, __buffer);
		  __result = __c(__buffer, __len);
		  alloc::deallocate(__buffer, __len * sizeof(_CharT));
		}
		__STL_UNWIND((alloc::deallocate(__buffer,
						__len * sizeof(_CharT))))
		return __result;
	    }
	default:
	    __stl_assert(false);
	    /*NOTREACHED*/
	    return false;
    }
}

rope 中定义了两个函数 _S_unref 和 _S_ref,两个函数都不做任何操作,只有在 __GC 没有被定义时,函数才会调用 _RopeRep 中同名函数的操作。这里假设 __GC 已被定义。

  static void _S_unref(_RopeRep*) {}
  static void _S_ref(_RopeRep*) {}

函数 _S_substring 用来获取 __base 表示的 rope 中从 __start 到 __endp1 之间的元素序列,但函数的返回值 _RopeRep* 类型的,这里要求返回的节点表示的 rope 中所包含的元素就正好是 __base 表示的 rope 中从 __start 到 __endp1 之间的元素。

令 __len = __base->_M_size,定义一个变量 __adj_endp1 和 一个常量 __lazy_threshold 等于 128 。如果 __endp1 > __len 且 start = 0(start 为 size_t 类型不存在负数的情况) ,则直接返回 __base,如果 __endp1 > __len 且 start > 0,则令 __adj_endp1 = __len。如果 __endp1 < __len ,则直接令 __adj_endp1 == __endp1 。

如果当前 __base 节点的类型为 _S_concat。令 __left 为 __base 的左子节点,令 __right 为 __base 的右子节点,__left_len = __left->_M_size 。如果 __adj_endp1 <= __left_len,则说明 __start 到 __adj_endp1 之间的所有节点都在左子树上,直接返回 _S_substring(__left, __start, __endp1),这里 __endp1 和 __adj_endp1 相等。

如果 __start >= __left_len,则说明 __start 到 __adj_endp1 之间的节点都在右子树上,直接返回 _S_substring(__right, __start - __left_len, __adj_endp1 - __left_len) 。

如果上面的两种情况都不成立,即 __start < __left_len < __adj_endp1,则说明 __start 到 __adj_endp1 之间的节点既在左子树上有又在右子树上有。用 _S_substring(__left, __start, __left_len) 的节点初始化 _Self_destructr_ptr 类型的节点(在定义了 __GC 的情况下 _Self_destruct_ptr 为 _RopeRep* 的类型别名) __left_result。用 _S_substring(__right, 0, __endp1 - __left_len) 的节点初始化 _Self_destruct_ptr 类型的节点 __right_result,再调用 _S_concat 函数将 __left_result 和 __right_result 表示的子树拼接成一棵包含了以 __left_result 为根的子树中所有元素和以 __right_result 为根的子树中的所有元素的树,并将根节点存放在 __result 中。

如果当前 __base 节点的类型为 _S_leaf ,令 __result_len 为 __start 到 __adj_endp1 之间的距离,如果 __result_len > __lazy_threshold ,则跳到 lazy 根据 __base 中的内容和 __start 以及 __adj_endp1 的位置构造一个新的 _S_substringfn 类型的新节点。否则令 __section 为当前节点 _M_data + __start 所在的地址。然后用 __section 和 __result_len 构造一个新的叶节点。

如果当前节点的类型为 _S_substringfn,同样令 __resutl_len 为 __start 到 __adj_endp1 之间的距离。如果 result_len > __lazy_threshold 则用__base 中的 _M_base 来构造一个新的 _S_substring 类型的节点,这里用 _M_base 来构造新节点是避免产生多层的 _S_substring 类型的节点。因为如果直接用 __base 来构造一个 _S_substring 类型的节点 __s 的话。则要获取 __s 中的节点,需调用 __s 中的 operator() 函数,而 __s 中的 operator() 函数会调用 base (__s->_M_base 指向 __base) 中的 operator() 函数,而 base 中的 operator() 函数会调用 __base->_M_base 的 operator() 函数。所以为了避免这种重复的调用,直接用 __base->_M_base 来构造 _S_substring 节点则可以少一层调用。如果 __result_len <= __lazy_threshold ,则进入到下一个分支处理,将当前节点当成 _S_function 类型来处理。

如果当前节点的类型为 _S_function,令 __result_len 为 __start 到 __adj_endp1 之间的距离,如果 __result_len > __lazy_threshold ,则跳到 lazy 构造一个新的 substring 类型的新节点。否则申请一片能够容纳 _S_rounded_up_size(__result_len) 个元素的节点(为结束标记也分配了额外的节点)。然后将当前节点中从 __start 位置开始长度为 __result_len 的元素序列读到从 __section 开始的位置。并设置好结束标记。并用 section 的内容构造一个 _S_leaf 类型的新节点返回。

如果在 switch 分支中跳到了 lazy ,则直接用 base, __start, adj_endp1 提供的信息构造一个 _S_substring 类型的新节点。

template <class _CharT, class _Alloc>
rope<_CharT,_Alloc>::_RopeRep*
rope<_CharT,_Alloc>::_S_substring(_RopeRep* __base, 
			       size_t __start, size_t __endp1)
{
    if (0 == __base) return 0;
    size_t __len = __base->_M_size;
    size_t __adj_endp1;
    const size_t __lazy_threshold = 128;
    
    if (__endp1 >= __len) {
	if (0 == __start) {
	    __base->_M_ref_nonnil();
	    return __base;
	} else {
	    __adj_endp1 = __len;
	}
    } else {
	__adj_endp1 = __endp1;
    }
    switch(__base->_M_tag) {
	case _RopeRep::_S_concat:
	    {
		_RopeConcatenation* __c = (_RopeConcatenation*)__base;
		_RopeRep* __left = __c->_M_left;
		_RopeRep* __right = __c->_M_right;
		size_t __left_len = __left->_M_size;
		_RopeRep* __result;

		if (__adj_endp1 <= __left_len) {
		    return _S_substring(__left, __start, __endp1);
		} else if (__start >= __left_len) {
		    return _S_substring(__right, __start - __left_len,
				  __adj_endp1 - __left_len);
		}
		_Self_destruct_ptr __left_result(
		  _S_substring(__left, __start, __left_len));
		_Self_destruct_ptr __right_result(
		  _S_substring(__right, 0, __endp1 - __left_len));
		__result = _S_concat(__left_result, __right_result);
		return __result;
	    }
	case _RopeRep::_S_leaf:
	    {
		_RopeLeaf* __l = (_RopeLeaf*)__base;
		_RopeLeaf* __result;
		size_t __result_len;
		if (__start >= __adj_endp1) return 0;
		__result_len = __adj_endp1 - __start;
		if (__result_len > __lazy_threshold) goto lazy;
		    const _CharT* __section = __l->_M_data + __start;
		    __result = _S_new_RopeLeaf(__section, __result_len,
					  __base->get_allocator());
		    __result->_M_c_string = 0;  // Not eos terminated.
		return __result;
	    }
	case _RopeRep::_S_substringfn:
	    // Avoid introducing multiple layers of substring nodes.
	    {
		_RopeSubstring* __old = (_RopeSubstring*)__base;
		size_t __result_len;
		if (__start >= __adj_endp1) return 0;
		__result_len = __adj_endp1 - __start;
		if (__result_len > __lazy_threshold) {
		    _RopeSubstring* __result =
			_S_new_RopeSubstring(__old->_M_base,
					  __start + __old->_M_start,
					  __adj_endp1 - __start,
					  __base->get_allocator());
		    return __result;

		} // *** else fall through: ***
	    }
	case _RopeRep::_S_function:
	    {
		_RopeFunction* __f = (_RopeFunction*)__base;
		_CharT* __section;
		size_t __result_len;
		if (__start >= __adj_endp1) return 0;
		__result_len = __adj_endp1 - __start;

		if (__result_len > __lazy_threshold) goto lazy;
		__section = (_CharT*)
			_Data_allocate(_S_rounded_up_size(__result_len));
		__STL_TRY {
		  (*(__f->_M_fn))(__start, __result_len, __section);
		}
		__STL_UNWIND(_RopeRep::__STL_FREE_STRING(
		       __section, __result_len, __base->get_allocator()));
		_S_cond_store_eos(__section[__result_len]);
		return _S_new_RopeLeaf(__section, __result_len,
				       __base->get_allocator());
	    }
    }
    /*NOTREACHED*/
    __stl_assert(false);
  lazy:
    {
	// Create substring node.
	return _S_new_RopeSubstring(__base, __start, __adj_endp1 - __start,
			       __base->get_allocator());
    }
}

STL 的 rope 分析(一)</br> STL 的 rope 分析(二)</br> STL 的 rope 分析(三)</br> STL 的 rope 分析(四)</br> STL 的 rope 分析(五)</br> STL 的 rope 分析(六)</br> STL 的 rope 分析(七)</br>