6599da043e
From-SVN: r14877
1511 lines
42 KiB
C++
1511 lines
42 KiB
C++
/*
|
|
* Copyright (c) 1997
|
|
* Silicon Graphics Computer Systems, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute and sell this software
|
|
* and its documentation for any purpose is hereby granted without fee,
|
|
* provided that the above copyright notice appear in all copies and
|
|
* that both that copyright notice and this permission notice appear
|
|
* in supporting documentation. Silicon Graphics makes no
|
|
* representations about the suitability of this software for any
|
|
* purpose. It is provided "as is" without express or implied warranty.
|
|
*/
|
|
|
|
#if defined(_MSC_VER)
|
|
# include <ostream>
|
|
#else
|
|
# include <iostream.h>
|
|
#endif
|
|
|
|
// Set buf_start, buf_end, and buf_ptr appropriately, filling tmp_buf
|
|
// if necessary. Assumes path_end[leaf_index] and leaf_pos are correct.
|
|
// Results in a valid buf_ptr if the iterator can be legitimately
|
|
// dereferenced.
|
|
template <class charT, class Alloc>
|
|
void __rope_iterator_base<charT,Alloc>::setbuf
|
|
(__rope_iterator_base<charT,Alloc> &x)
|
|
{
|
|
const RopeBase * leaf = x.path_end[x.leaf_index];
|
|
size_t leaf_pos = x.leaf_pos;
|
|
size_t pos = x.current_pos;
|
|
|
|
switch(leaf -> tag) {
|
|
case RopeBase::leaf:
|
|
x.buf_start = ((__rope_RopeLeaf<charT,Alloc> *)leaf) -> data;
|
|
x.buf_ptr = x.buf_start + (pos - leaf_pos);
|
|
x.buf_end = x.buf_start + leaf -> size;
|
|
break;
|
|
case RopeBase::function:
|
|
case RopeBase::substringfn:
|
|
{
|
|
size_t len = iterator_buf_len;
|
|
size_t buf_start_pos = leaf_pos;
|
|
size_t leaf_end = leaf_pos + leaf -> size;
|
|
char_producer<charT> *fn =
|
|
((__rope_RopeFunction<charT,Alloc> *)leaf) -> fn;
|
|
|
|
if (buf_start_pos + len <= pos) {
|
|
buf_start_pos = pos - len/4;
|
|
if (buf_start_pos + len > leaf_end) {
|
|
buf_start_pos = leaf_end - len;
|
|
}
|
|
}
|
|
if (buf_start_pos + len > leaf_end) {
|
|
len = leaf_end - buf_start_pos;
|
|
}
|
|
(*fn)(buf_start_pos - leaf_pos, len, x.tmp_buf);
|
|
x.buf_ptr = x.tmp_buf + (pos - buf_start_pos);
|
|
x.buf_start = x.tmp_buf;
|
|
x.buf_end = x.tmp_buf + len;
|
|
}
|
|
break;
|
|
default:
|
|
__stl_assert(0);
|
|
}
|
|
}
|
|
|
|
// Set path and buffer inside a rope iterator. We assume that
|
|
// pos and root are already set.
|
|
template <class charT, class Alloc>
|
|
void __rope_iterator_base<charT,Alloc>::setcache
|
|
(__rope_iterator_base<charT,Alloc> &x)
|
|
{
|
|
const RopeBase * path[RopeBase::max_rope_depth+1];
|
|
const RopeBase * curr_rope;
|
|
int curr_depth = -1; /* index into path */
|
|
size_t curr_start_pos = 0;
|
|
size_t pos = x.current_pos;
|
|
unsigned char dirns = 0; // Bit vector indicating right turns in the path
|
|
|
|
__stl_assert(pos <= x.root -> size);
|
|
if (pos >= x.root -> size) {
|
|
x.buf_ptr = 0;
|
|
return;
|
|
}
|
|
curr_rope = x.root;
|
|
if (0 != curr_rope -> c_string) {
|
|
/* Treat the root as a leaf. */
|
|
x.buf_start = curr_rope -> c_string;
|
|
x.buf_end = curr_rope -> c_string + curr_rope -> size;
|
|
x.buf_ptr = curr_rope -> c_string + pos;
|
|
x.path_end[0] = curr_rope;
|
|
x.leaf_index = 0;
|
|
x.leaf_pos = 0;
|
|
return;
|
|
}
|
|
for(;;) {
|
|
++curr_depth;
|
|
__stl_assert(curr_depth <= RopeBase::max_rope_depth);
|
|
path[curr_depth] = curr_rope;
|
|
switch(curr_rope -> tag) {
|
|
case RopeBase::leaf:
|
|
case RopeBase::function:
|
|
case RopeBase::substringfn:
|
|
x.leaf_pos = curr_start_pos;
|
|
goto done;
|
|
case RopeBase::concat:
|
|
{
|
|
__rope_RopeConcatenation<charT,Alloc> *c =
|
|
(__rope_RopeConcatenation<charT,Alloc> *)curr_rope;
|
|
RopeBase * left = c -> left;
|
|
size_t left_len = left -> size;
|
|
|
|
dirns <<= 1;
|
|
if (pos >= curr_start_pos + left_len) {
|
|
dirns |= 1;
|
|
curr_rope = c -> right;
|
|
curr_start_pos += left_len;
|
|
} else {
|
|
curr_rope = left;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
// Copy last section of path into path_end.
|
|
{
|
|
int i = -1;
|
|
int j = curr_depth + 1 - path_cache_len;
|
|
|
|
if (j < 0) j = 0;
|
|
while (j <= curr_depth) {
|
|
x.path_end[++i] = path[j++];
|
|
}
|
|
x.leaf_index = i;
|
|
}
|
|
x.path_directions = dirns;
|
|
setbuf(x);
|
|
}
|
|
|
|
// Specialized version of the above. Assumes that
|
|
// the path cache is valid for the previous position.
|
|
template <class charT, class Alloc>
|
|
void __rope_iterator_base<charT,Alloc>::setcache_for_incr
|
|
(__rope_iterator_base<charT,Alloc> &x)
|
|
{
|
|
int current_index = x.leaf_index;
|
|
const RopeBase * current_node = x.path_end[current_index];
|
|
size_t len = current_node -> size;
|
|
size_t node_start_pos = x.leaf_pos;
|
|
unsigned char dirns = x.path_directions;
|
|
__rope_RopeConcatenation<charT,Alloc> * c;
|
|
|
|
__stl_assert(x.current_pos <= x.root -> size);
|
|
if (x.current_pos - node_start_pos < len) {
|
|
/* More stuff in this leaf, we just didn't cache it. */
|
|
setbuf(x);
|
|
return;
|
|
}
|
|
__stl_assert(node_start_pos + len == x.current_pos);
|
|
// node_start_pos is starting position of last_node.
|
|
while (--current_index >= 0) {
|
|
if (!(dirns & 1) /* Path turned left */) break;
|
|
current_node = x.path_end[current_index];
|
|
c = (__rope_RopeConcatenation<charT,Alloc> *)current_node;
|
|
// Otherwise we were in the right child. Thus we should pop
|
|
// the concatenation node.
|
|
node_start_pos -= c -> left -> size;
|
|
dirns >>= 1;
|
|
}
|
|
if (current_index < 0) {
|
|
// We underflowed the cache. Punt.
|
|
setcache(x);
|
|
return;
|
|
}
|
|
current_node = x.path_end[current_index];
|
|
c = (__rope_RopeConcatenation<charT,Alloc> *)current_node;
|
|
// current_node is a concatenation node. We are positioned on the first
|
|
// character in its right child.
|
|
// node_start_pos is starting position of current_node.
|
|
node_start_pos += c -> left -> size;
|
|
current_node = c -> right;
|
|
x.path_end[++current_index] = current_node;
|
|
dirns |= 1;
|
|
while (RopeBase::concat == current_node -> tag) {
|
|
++current_index;
|
|
if (path_cache_len == current_index) {
|
|
int i;
|
|
for (i = 0; i < path_cache_len-1; i++) {
|
|
x.path_end[i] = x.path_end[i+1];
|
|
}
|
|
--current_index;
|
|
}
|
|
current_node =
|
|
((__rope_RopeConcatenation<charT,Alloc> *)current_node) -> left;
|
|
x.path_end[current_index] = current_node;
|
|
dirns <<= 1;
|
|
// node_start_pos is unchanged.
|
|
}
|
|
x.leaf_index = current_index;
|
|
x.leaf_pos = node_start_pos;
|
|
x.path_directions = dirns;
|
|
setbuf(x);
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
void __rope_iterator_base<charT,Alloc>::incr(size_t n) {
|
|
current_pos += n;
|
|
if (0 != buf_ptr) {
|
|
size_t chars_left = buf_end - buf_ptr;
|
|
if (chars_left > n) {
|
|
buf_ptr += n;
|
|
} else if (chars_left == n) {
|
|
buf_ptr += n;
|
|
setcache_for_incr(*this);
|
|
} else {
|
|
buf_ptr = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
void __rope_iterator_base<charT,Alloc>::decr(size_t n) {
|
|
if (0 != buf_ptr) {
|
|
size_t chars_left = buf_ptr - buf_start;
|
|
if (chars_left >= n) {
|
|
buf_ptr -= n;
|
|
} else {
|
|
buf_ptr = 0;
|
|
}
|
|
}
|
|
current_pos -= n;
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
void __rope_iterator<charT,Alloc>::check() {
|
|
if (root_rope -> tree_ptr != root) {
|
|
// Rope was modified. Get things fixed up.
|
|
RopeBase::unref(root);
|
|
root = root_rope -> tree_ptr;
|
|
RopeBase::ref(root);
|
|
buf_ptr = 0;
|
|
}
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
inline __rope_const_iterator<charT, Alloc>::__rope_const_iterator
|
|
(const __rope_iterator<charT,Alloc> & x)
|
|
: __rope_iterator_base<charT,Alloc>(x) { }
|
|
|
|
template <class charT, class Alloc>
|
|
inline __rope_iterator<charT,Alloc>::__rope_iterator
|
|
(rope<charT,Alloc>& r, size_t pos)
|
|
: __rope_iterator_base<charT,Alloc>(r.tree_ptr, pos), root_rope(&r) {
|
|
RopeBase::ref(root);
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
inline size_t rope<charT,Alloc>::char_ptr_len(const charT *s)
|
|
{
|
|
const charT *p = s;
|
|
|
|
while (!is0(*p)) { ++p; }
|
|
return(p - s);
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeLeaf *
|
|
rope<charT,Alloc>::RopeLeaf_from_char_ptr(__GC_CONST charT *s, size_t size)
|
|
{
|
|
RopeLeaf *t = LAlloc::allocate();
|
|
|
|
t -> tag = RopeBase::leaf;
|
|
if (__is_basic_char_type((charT *)0)) {
|
|
// already eos terminated.
|
|
t -> c_string = s;
|
|
} else {
|
|
t -> c_string = 0;
|
|
}
|
|
t -> is_balanced = true;
|
|
t -> depth = 0;
|
|
t -> size = size;
|
|
t -> data = s;
|
|
# ifndef __GC
|
|
t -> refcount = 1;
|
|
t -> init_refcount_lock();
|
|
# endif
|
|
return (t);
|
|
}
|
|
|
|
# ifdef __GC
|
|
template <class charT, class Alloc>
|
|
void __rope_RopeBase<charT,Alloc>::fn_finalization_proc(void * tree, void *)
|
|
{
|
|
delete ((__rope_RopeFunction<charT,Alloc> *)tree) -> fn;
|
|
}
|
|
# endif
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeFunction *
|
|
rope<charT,Alloc>::RopeFunction_from_fn
|
|
(char_producer<charT> *fn, size_t size, bool delete_fn)
|
|
{
|
|
if (0 == size) return 0;
|
|
RopeFunction *t = FAlloc::allocate();
|
|
t -> tag = RopeBase::function;
|
|
t -> c_string = 0;
|
|
t -> is_balanced = true;
|
|
t -> depth = 0;
|
|
t -> size = size;
|
|
t -> fn = fn;
|
|
# ifdef __GC
|
|
if (delete_fn) {
|
|
GC_REGISTER_FINALIZER(t, RopeBase::fn_finalization_proc, 0, 0, 0);
|
|
}
|
|
# else
|
|
t -> delete_when_done = delete_fn;
|
|
t -> refcount = 1;
|
|
t -> init_refcount_lock();
|
|
# endif
|
|
return (t);
|
|
}
|
|
|
|
#ifndef __GC
|
|
|
|
template <class charT, class Alloc>
|
|
inline void __rope_RopeBase<charT,Alloc>::free_c_string()
|
|
{
|
|
charT * cstr = c_string;
|
|
if (0 != cstr) {
|
|
size_t sz = size + 1;
|
|
destroy(cstr, cstr + sz);
|
|
Alloc::deallocate(cstr, sz);
|
|
}
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
inline void __rope_RopeBase<charT,Alloc>::free_string(charT* s, size_t n)
|
|
{
|
|
if (!__is_basic_char_type((charT *)0)) {
|
|
destroy(s, s + n);
|
|
}
|
|
Alloc::deallocate(s, rounded_up_size(n));
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
void __rope_RopeBase<charT,Alloc>::free_tree()
|
|
{
|
|
switch(tag) {
|
|
case leaf:
|
|
{
|
|
__rope_RopeLeaf<charT,Alloc> * l =
|
|
(__rope_RopeLeaf<charT,Alloc> *)this;
|
|
charT * d = l -> data;
|
|
|
|
if (d != c_string) {
|
|
free_c_string();
|
|
}
|
|
free_string(d, size);
|
|
LAlloc::deallocate(l);
|
|
}
|
|
break;
|
|
case concat:
|
|
{
|
|
__rope_RopeConcatenation<charT,Alloc> * c =
|
|
(__rope_RopeConcatenation<charT,Alloc> *)this;
|
|
__rope_RopeBase * left = c -> left;
|
|
__rope_RopeBase * right = c -> right;
|
|
free_c_string();
|
|
left -> unref_nonnil();
|
|
right -> unref_nonnil();
|
|
CAlloc::deallocate(c);
|
|
}
|
|
break;
|
|
case function:
|
|
{
|
|
__rope_RopeFunction<charT,Alloc> * fn =
|
|
(__rope_RopeFunction<charT,Alloc> *)this;
|
|
free_c_string();
|
|
if ( fn -> delete_when_done) {
|
|
delete fn -> fn;
|
|
}
|
|
FAlloc::deallocate(fn);
|
|
break;
|
|
}
|
|
case substringfn:
|
|
{
|
|
__rope_RopeSubstring<charT,Alloc> * ss =
|
|
(__rope_RopeSubstring<charT,Alloc> *)this;
|
|
__rope_RopeBase *base = ss -> base;
|
|
free_c_string();
|
|
base -> unref_nonnil();
|
|
SAlloc::deallocate(ss);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
|
|
template <class charT, class Alloc>
|
|
inline void __rope_RopeBase<charT,Alloc>::free_string(charT* s, size_t n)
|
|
{}
|
|
|
|
#endif
|
|
|
|
|
|
// Concatenate a C string onto a leaf rope by copying the rope data.
|
|
// Used for short ropes.
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeLeaf *
|
|
rope<charT,Alloc>::leaf_concat_char_iter
|
|
(RopeLeaf * r, const charT * iter, size_t len)
|
|
{
|
|
size_t old_len = r -> size;
|
|
charT * new_data = (charT *)
|
|
Alloc::allocate(rounded_up_size(old_len + len));
|
|
RopeLeaf * result;
|
|
|
|
uninitialized_copy_n(r -> data, old_len, new_data);
|
|
uninitialized_copy_n(iter, len, new_data + old_len);
|
|
__cond_store_eos(new_data[old_len + len]);
|
|
__STL_TRY
|
|
result = RopeLeaf_from_char_ptr(new_data, old_len + len);
|
|
__STL_UNWIND(RopeBase::free_string(new_data, old_len + len));
|
|
return result;
|
|
}
|
|
|
|
#ifndef __GC
|
|
// As above, but it's OK to clobber original if refcount is 1
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeLeaf *
|
|
rope<charT,Alloc>::destr_leaf_concat_char_iter
|
|
(RopeLeaf * r, const charT * iter, size_t len)
|
|
{
|
|
__stl_assert(r -> refcount >= 1);
|
|
if (r -> refcount > 1) return leaf_concat_char_iter(r, iter, len);
|
|
size_t old_len = r -> size;
|
|
if (allocated_capacity(old_len) >= old_len + len) {
|
|
// The space has been partially initialized for the standard
|
|
// character types. But that doesn't matter for those types.
|
|
uninitialized_copy_n(iter, len, r -> data + old_len);
|
|
if (__is_basic_char_type((charT *)0)) {
|
|
__cond_store_eos(r -> data[old_len + len]);
|
|
__stl_assert(r -> c_string == r -> data);
|
|
} else if (r -> c_string != r -> data && 0 != r -> c_string) {
|
|
r -> free_c_string();
|
|
r -> c_string = 0;
|
|
}
|
|
r -> size = old_len + len;
|
|
__stl_assert(r -> refcount == 1);
|
|
r -> refcount = 2;
|
|
return r;
|
|
} else {
|
|
RopeLeaf * result = leaf_concat_char_iter(r, iter, len);
|
|
__stl_assert(result -> refcount == 1);
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Assumes left and right are not 0.
|
|
// Result has ref count 1.
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeBase *
|
|
rope<charT,Alloc>::tree_concat (RopeBase * left, RopeBase * right)
|
|
{
|
|
RopeConcatenation * result = CAlloc::allocate();
|
|
unsigned char child_depth = left -> depth;
|
|
size_t rsize;
|
|
|
|
result -> tag = RopeBase::concat;
|
|
result -> c_string = 0;
|
|
result -> is_balanced = false;
|
|
result -> size = rsize = left -> size + right -> size;
|
|
if (right -> depth > child_depth) child_depth = right -> depth;
|
|
unsigned char depth = child_depth + 1;
|
|
result -> depth = depth;
|
|
result -> left = left;
|
|
result -> right = right;
|
|
# ifndef __GC
|
|
result -> refcount = 1;
|
|
result -> init_refcount_lock();
|
|
# endif
|
|
if (depth > 20 && (rsize < 1000 || depth > RopeBase::max_rope_depth)) {
|
|
RopeBase * balanced;
|
|
|
|
__STL_TRY
|
|
balanced = balance(result);
|
|
# ifndef __GC
|
|
if (result != balanced) {
|
|
__stl_assert(1 == result -> refcount
|
|
&& 1 == balanced -> refcount);
|
|
}
|
|
# endif
|
|
__STL_ALWAYS(result -> unref_nonnil());
|
|
return balanced;
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeBase * rope<charT,Alloc>::concat_char_iter
|
|
(RopeBase * r, const charT *s, size_t slen)
|
|
{
|
|
RopeBase *result;
|
|
if (0 == slen) {
|
|
ref(r);
|
|
return r;
|
|
}
|
|
if (0 == r) return RopeLeaf_from_char_ptr(alloc_copy(s, slen), slen);
|
|
if (RopeBase::leaf == r -> tag && r -> size + slen <= copy_max) {
|
|
result = leaf_concat_char_iter((RopeLeaf *)r, s, slen);
|
|
# ifndef __GC
|
|
__stl_assert(1 == result -> refcount);
|
|
# endif
|
|
return result;
|
|
}
|
|
if (RopeBase::concat == r -> tag
|
|
&& RopeBase::leaf == ((RopeConcatenation *)r) -> right -> tag) {
|
|
RopeLeaf *right = (RopeLeaf *)(((RopeConcatenation *)r) -> right);
|
|
if (right -> size + slen <= copy_max) {
|
|
RopeBase * left = ((RopeConcatenation *)r) -> left;
|
|
left -> ref_nonnil();
|
|
__STL_TRY
|
|
result = tree_concat(left,
|
|
leaf_concat_char_iter((RopeLeaf *)right,
|
|
s, slen));
|
|
__STL_UNWIND(unref(left));
|
|
# ifndef __GC
|
|
__stl_assert(1 == result -> refcount);
|
|
# endif
|
|
return result;
|
|
}
|
|
}
|
|
__STL_TRY
|
|
r -> ref_nonnil();
|
|
result = tree_concat(r, RopeLeaf_from_char_ptr(alloc_copy(s, slen),
|
|
slen));
|
|
__STL_UNWIND(unref(r));
|
|
# ifndef __GC
|
|
__stl_assert(1 == result -> refcount);
|
|
# endif
|
|
return result;
|
|
}
|
|
|
|
#ifndef __GC
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeBase * rope<charT,Alloc>
|
|
::destr_concat_char_iter
|
|
(RopeBase * r, const charT *s, size_t slen)
|
|
{
|
|
RopeBase *result;
|
|
if (0 == r) return RopeLeaf_from_char_ptr(alloc_copy(s, slen), slen);
|
|
size_t count = r -> refcount;
|
|
size_t orig_size = r -> size;
|
|
__stl_assert(count >= 1);
|
|
if (count > 1) return concat_char_iter(r, s, slen);
|
|
if (0 == slen) {
|
|
r -> refcount = 2; // One more than before
|
|
return r;
|
|
}
|
|
if (orig_size + slen <= copy_max && RopeBase::leaf == r -> tag) {
|
|
result = destr_leaf_concat_char_iter((RopeLeaf *)r, s, slen);
|
|
return result;
|
|
}
|
|
if (RopeBase::concat == r -> tag) {
|
|
RopeLeaf *right = (RopeLeaf *)(((RopeConcatenation *)r) -> right);
|
|
if (RopeBase::leaf == right -> tag
|
|
&& right -> size + slen <= copy_max) {
|
|
RopeBase * new_right = destr_leaf_concat_char_iter(right, s, slen);
|
|
if (right == new_right) {
|
|
__stl_assert(new_right -> refcount == 2);
|
|
new_right -> refcount = 1;
|
|
} else {
|
|
__stl_assert(new_right -> refcount >= 1);
|
|
right -> unref_nonnil();
|
|
}
|
|
__stl_assert(r -> refcount == 1);
|
|
r -> refcount = 2; // One more than before.
|
|
((RopeConcatenation *)r) -> right = new_right;
|
|
r -> size = orig_size + slen;
|
|
if (0 != r -> c_string) {
|
|
r -> free_c_string();
|
|
r -> c_string = 0;
|
|
}
|
|
return r;
|
|
}
|
|
}
|
|
charT * cpy = alloc_copy(s, slen);
|
|
r -> ref_nonnil();
|
|
__STL_TRY
|
|
result = tree_concat(r, RopeLeaf_from_char_ptr(cpy, slen));
|
|
__STL_UNWIND(unref(r); RopeBase::free_string(cpy,slen))
|
|
__stl_assert(1 == result -> refcount);
|
|
return result;
|
|
}
|
|
#endif /* !__GC */
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeBase *
|
|
rope<charT,Alloc>::concat(RopeBase * left, RopeBase * right)
|
|
{
|
|
if (0 == left) {
|
|
ref(right);
|
|
return right;
|
|
}
|
|
if (0 == right) {
|
|
left -> ref_nonnil();
|
|
return left;
|
|
}
|
|
if (RopeBase::leaf == right -> tag) {
|
|
if (RopeBase::leaf == left -> tag) {
|
|
if (right -> size + left -> size <= copy_max) {
|
|
return leaf_concat_char_iter((RopeLeaf *)left,
|
|
((RopeLeaf *)right) -> data,
|
|
right -> size);
|
|
}
|
|
} else if (RopeBase::concat == left -> tag
|
|
&& RopeBase::leaf ==
|
|
((RopeConcatenation *)left) -> right -> tag) {
|
|
RopeLeaf * leftright =
|
|
(RopeLeaf *)(((RopeConcatenation *)left) -> right);
|
|
if (leftright -> size + right -> size <= copy_max) {
|
|
RopeBase * leftleft = ((RopeConcatenation *)left) -> left;
|
|
RopeBase * rest = leaf_concat_char_iter(leftright,
|
|
((RopeLeaf *)right) -> data,
|
|
right -> size);
|
|
leftleft -> ref_nonnil();
|
|
__STL_TRY
|
|
return(tree_concat(leftleft, rest));
|
|
__STL_UNWIND(unref(left); unref(rest))
|
|
}
|
|
}
|
|
}
|
|
left -> ref_nonnil();
|
|
right -> ref_nonnil();
|
|
return(tree_concat(left, right));
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeBase *
|
|
rope<charT,Alloc>::substring(RopeBase * base, size_t start, size_t endp1)
|
|
{
|
|
if (0 == base) return 0;
|
|
size_t len = base -> size;
|
|
size_t adj_endp1;
|
|
const size_t lazy_threshold = 128;
|
|
|
|
if (endp1 >= len) {
|
|
if (0 == start) {
|
|
base -> ref_nonnil();
|
|
return base;
|
|
} else {
|
|
adj_endp1 = len;
|
|
}
|
|
} else {
|
|
adj_endp1 = endp1;
|
|
}
|
|
switch(base -> tag) {
|
|
case RopeBase::concat:
|
|
{
|
|
RopeConcatenation *c = (RopeConcatenation *)base;
|
|
RopeBase *left = c -> left;
|
|
RopeBase *right = c -> right;
|
|
size_t left_len = left -> size;
|
|
RopeBase * result;
|
|
|
|
if (adj_endp1 <= left_len) {
|
|
return substring(left, start, endp1);
|
|
} else if (start >= left_len) {
|
|
return substring(right, start - left_len,
|
|
adj_endp1 - left_len);
|
|
}
|
|
self_destruct_ptr left_result(substring(left, start,
|
|
left_len));
|
|
self_destruct_ptr right_result(
|
|
substring(right, 0, endp1 - left_len));
|
|
result = concat(left_result, right_result);
|
|
# ifndef __GC
|
|
__stl_assert(1 == result -> refcount);
|
|
# endif
|
|
return result;
|
|
}
|
|
case RopeBase::leaf:
|
|
{
|
|
RopeLeaf * l = (RopeLeaf *)base;
|
|
RopeLeaf * result;
|
|
__GC_CONST charT *section;
|
|
size_t result_len;
|
|
if (start >= adj_endp1) return 0;
|
|
result_len = adj_endp1 - start;
|
|
if (result_len > lazy_threshold) goto lazy;
|
|
# ifdef __GC
|
|
section = l -> data + start;
|
|
result = RopeLeaf_from_char_ptr(section, result_len);
|
|
result -> c_string = 0; // Not eos terminated.
|
|
# else
|
|
section = alloc_copy(l -> data + start, result_len);
|
|
// We should sometimes create substring node instead.
|
|
__STL_TRY
|
|
result = RopeLeaf_from_char_ptr(section, result_len);
|
|
__STL_UNWIND(RopeBase::free_string(section, result_len))
|
|
# endif
|
|
return result;
|
|
}
|
|
case RopeBase::substringfn:
|
|
// Avoid introducing mutiple 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 * space = SAlloc::allocate();
|
|
RopeSubstring * result =
|
|
new(space) RopeSubstring(old -> base,
|
|
start + old -> start,
|
|
adj_endp1 - start);
|
|
return result;
|
|
} // else fall through:
|
|
}
|
|
case RopeBase::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 *)
|
|
Alloc::allocate(rounded_up_size(result_len));
|
|
__STL_TRY
|
|
(*(f -> fn))(start, result_len, section);
|
|
__STL_UNWIND(RopeBase::free_string(section, result_len));
|
|
__cond_store_eos(section[result_len]);
|
|
return RopeLeaf_from_char_ptr(section, result_len);
|
|
}
|
|
}
|
|
/*NOTREACHED*/
|
|
__stl_assert(false);
|
|
lazy:
|
|
{
|
|
// Create substring node.
|
|
RopeSubstring * space = SAlloc::allocate();
|
|
RopeSubstring * result = new(space) RopeSubstring(base, start,
|
|
adj_endp1 - start);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
template<class charT>
|
|
class __rope_flatten_char_consumer : public __rope_char_consumer<charT> {
|
|
private:
|
|
charT * buf_ptr;
|
|
public:
|
|
charT * buffer;
|
|
__rope_flatten_char_consumer(charT * buffer) {
|
|
buf_ptr = buffer;
|
|
};
|
|
~__rope_flatten_char_consumer() {}
|
|
bool operator() (const charT* leaf, size_t n) {
|
|
uninitialized_copy_n(leaf, n, buf_ptr);
|
|
buf_ptr += n;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template<class charT>
|
|
class __rope_find_char_char_consumer : public __rope_char_consumer<charT> {
|
|
private:
|
|
charT pattern;
|
|
public:
|
|
size_t count; // Number of nonmatching characters
|
|
__rope_find_char_char_consumer(charT p) : pattern(p), count(0) {}
|
|
~__rope_find_char_char_consumer() {}
|
|
bool operator() (const charT* leaf, size_t n) {
|
|
size_t i;
|
|
for (i = 0; i < n; i++) {
|
|
if (leaf[i] == pattern) {
|
|
count += i; return false;
|
|
}
|
|
}
|
|
count += n; return true;
|
|
}
|
|
};
|
|
|
|
template<class charT>
|
|
class __rope_insert_char_consumer : public __rope_char_consumer<charT> {
|
|
private:
|
|
typedef ostream insert_ostream;
|
|
insert_ostream & o;
|
|
public:
|
|
charT * buffer;
|
|
__rope_insert_char_consumer(insert_ostream & writer) : o(writer) {};
|
|
~__rope_insert_char_consumer() { };
|
|
// Caller is presumed to own the ostream
|
|
bool operator() (const charT* leaf, size_t n);
|
|
// Returns true to continue traversal.
|
|
};
|
|
|
|
template<class charT>
|
|
bool __rope_insert_char_consumer<charT>::operator()
|
|
(const charT * leaf, size_t n)
|
|
{
|
|
size_t i;
|
|
// We assume that formatting is set up correctly for each element.
|
|
for (i = 0; i < n; i++) o << leaf[i];
|
|
return true;
|
|
}
|
|
|
|
inline bool __rope_insert_char_consumer<char>::operator()
|
|
(const char * leaf, size_t n)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < n; i++) o.put(leaf[i]);
|
|
return true;
|
|
}
|
|
|
|
#if !defined(_MSC_VER) && !defined(__BORLANDC__)
|
|
// I couldn't get this to work work with the VC++ version of basic_ostream.
|
|
inline bool __rope_insert_char_consumer<wchar_t>::operator()
|
|
(const wchar_t * leaf, size_t n)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < n; i++) o.put(leaf[i]);
|
|
return true;
|
|
}
|
|
#endif /* !_MSC_VER && !BORLAND */
|
|
|
|
template <class charT, class Alloc>
|
|
bool rope<charT, Alloc>::apply_to_pieces(
|
|
__rope_char_consumer<charT>& c,
|
|
const RopeBase * r,
|
|
size_t begin, size_t end)
|
|
{
|
|
if (0 == r) return true;
|
|
switch(r -> tag) {
|
|
case RopeBase::concat:
|
|
{
|
|
RopeConcatenation *conc = (RopeConcatenation *)r;
|
|
RopeBase *left = conc -> left;
|
|
size_t left_len = left -> size;
|
|
if (begin < left_len) {
|
|
size_t left_end = min(left_len, end);
|
|
if (!apply_to_pieces(c, left, begin, left_end)) {
|
|
return false;
|
|
}
|
|
}
|
|
if (end > left_len) {
|
|
RopeBase *right = conc -> right;
|
|
size_t right_start = max(left_len, begin);
|
|
if (!apply_to_pieces(c, right,
|
|
right_start - left_len,
|
|
end - left_len)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
case RopeBase::leaf:
|
|
{
|
|
RopeLeaf * l = (RopeLeaf *)r;
|
|
return c(l -> data + begin, end - begin);
|
|
}
|
|
case RopeBase::function:
|
|
case RopeBase::substringfn:
|
|
{
|
|
RopeFunction * f = (RopeFunction *)r;
|
|
size_t len = end - begin;
|
|
bool result;
|
|
charT * buffer = (charT *)
|
|
Alloc::allocate(len * sizeof(charT));
|
|
__STL_TRY
|
|
(*(f -> fn))(begin, end, buffer);
|
|
result = c(buffer, len);
|
|
__STL_ALWAYS(Alloc::deallocate(buffer, len * sizeof(charT)))
|
|
return result;
|
|
}
|
|
default:
|
|
__stl_assert(false);
|
|
/*NOTREACHED*/
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline void __rope_fill(ostream& o, size_t n)
|
|
{
|
|
char f = cout.fill();
|
|
size_t i;
|
|
|
|
for (i = 0; i < n; i++) o.put(f);
|
|
}
|
|
|
|
|
|
template <class charT> inline bool __rope_is_simple(charT *c) { return false; }
|
|
inline bool __rope_is_simple(char * c) { return true; }
|
|
inline bool __rope_is_simple(wchar_t * c) { return true; }
|
|
|
|
|
|
template<class charT, class Alloc>
|
|
ostream& operator<< (ostream& o, const rope<charT, Alloc>& r)
|
|
{
|
|
size_t w = o.width();
|
|
bool left = o.flags() & ios::left;
|
|
size_t pad_len;
|
|
size_t rope_len = r.size();
|
|
__rope_insert_char_consumer<charT> c(o);
|
|
bool is_simple = __rope_is_simple((charT *)0);
|
|
|
|
if (rope_len < w) {
|
|
pad_len = w - rope_len;
|
|
} else {
|
|
pad_len = 0;
|
|
}
|
|
if (!is_simple) o.width(w/rope_len);
|
|
__STL_TRY
|
|
if (is_simple && !left && pad_len > 0) {
|
|
__rope_fill(o, pad_len);
|
|
}
|
|
r.apply_to_pieces(0, r.size(), c);
|
|
if (is_simple && left && pad_len > 0) {
|
|
__rope_fill(o, pad_len);
|
|
}
|
|
__STL_ALWAYS(if (!is_simple) o.width(w))
|
|
return o;
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
charT *
|
|
rope<charT,Alloc>::flatten(RopeBase * r,
|
|
size_t start, size_t len,
|
|
charT * buffer)
|
|
{
|
|
__rope_flatten_char_consumer<charT> c(buffer);
|
|
apply_to_pieces(c, r, start, start + len);
|
|
return(buffer + len);
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
size_t
|
|
rope<charT,Alloc>::find(charT pattern, size_t start) const
|
|
{
|
|
__rope_find_char_char_consumer<charT> c(pattern);
|
|
apply_to_pieces(c, tree_ptr, start, size());
|
|
return start + c.count;
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
charT *
|
|
rope<charT,Alloc>::flatten(RopeBase * r, charT * buffer)
|
|
{
|
|
if (0 == r) return buffer;
|
|
switch(r -> tag) {
|
|
case RopeBase::concat:
|
|
{
|
|
RopeConcatenation *c = (RopeConcatenation *)r;
|
|
RopeBase *left = c -> left;
|
|
RopeBase *right = c -> right;
|
|
charT * rest = flatten(left, buffer);
|
|
return flatten(right, rest);
|
|
}
|
|
case RopeBase::leaf:
|
|
{
|
|
RopeLeaf * l = (RopeLeaf *)r;
|
|
return copy_n(l -> data, l -> size, buffer);
|
|
}
|
|
case RopeBase::function:
|
|
case RopeBase::substringfn:
|
|
// We dont yet do anything with substring nodes.
|
|
// This needs to be fixed before ropefiles will work well.
|
|
{
|
|
RopeFunction * f = (RopeFunction *)r;
|
|
(*(f -> fn))(0, f -> size, buffer);
|
|
return buffer + f -> size;
|
|
}
|
|
default:
|
|
__stl_assert(false);
|
|
/*NOTREACHED*/
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
// This needs work for charT != char
|
|
template <class charT, class Alloc>
|
|
void
|
|
rope<charT,Alloc>::dump(RopeBase * r, int indent)
|
|
{
|
|
for (int i = 0; i < indent; i++) putchar(' ');
|
|
if (0 == r) {
|
|
printf("NULL\n"); return;
|
|
}
|
|
if (RopeBase::concat == r -> tag) {
|
|
RopeConcatenation *c = (RopeConcatenation *)r;
|
|
RopeBase *left = c -> left;
|
|
RopeBase *right = c -> right;
|
|
|
|
# ifdef __GC
|
|
printf("Concatenation %p (depth = %d, len = %ld, %s balanced)\n",
|
|
r, r -> depth, r -> size, r -> is_balanced? "" : "not");
|
|
# else
|
|
printf("Concatenation %p (rc = %ld, depth = %d, len = %ld, %s balanced)\n",
|
|
r, r -> refcount, r -> depth, r -> size,
|
|
r -> is_balanced? "" : "not");
|
|
# endif
|
|
dump(left, indent + 2);
|
|
dump(right, indent + 2);
|
|
return;
|
|
} else {
|
|
char * kind;
|
|
|
|
switch (r -> tag) {
|
|
case RopeBase::leaf:
|
|
kind = "Leaf";
|
|
break;
|
|
case RopeBase::function:
|
|
kind = "Function";
|
|
break;
|
|
case RopeBase::substringfn:
|
|
kind = "Function representing substring";
|
|
break;
|
|
default:
|
|
kind = "(corrupted kind field!)";
|
|
}
|
|
# ifdef __GC
|
|
printf("%s %p (depth = %d, len = %ld) ",
|
|
kind, r, r -> depth, r -> size);
|
|
# else
|
|
printf("%s %p (rc = %ld, depth = %d, len = %ld) ",
|
|
kind, r, r -> refcount, r -> depth, r -> size);
|
|
# endif
|
|
if (__is_one_byte_char_type((charT *)0)) {
|
|
const int max_len = 40;
|
|
self_destruct_ptr prefix(substring(r, 0, max_len));
|
|
charT buffer[max_len + 1];
|
|
bool too_big = r -> size > prefix-> size;
|
|
|
|
flatten(prefix, buffer);
|
|
buffer[prefix -> size] = __eos((charT *)0);
|
|
printf("%s%s\n", (char *)buffer, too_big? "...\n" : "\n");
|
|
} else {
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
const unsigned long
|
|
rope<charT,Alloc>::min_len[__rope_RopeBase<charT,Alloc>::max_rope_depth + 1] = {
|
|
/* 0 */1, /* 1 */2, /* 2 */3, /* 3 */5, /* 4 */8, /* 5 */13, /* 6 */21,
|
|
/* 7 */34, /* 8 */55, /* 9 */89, /* 10 */144, /* 11 */233, /* 12 */377,
|
|
/* 13 */610, /* 14 */987, /* 15 */1597, /* 16 */2584, /* 17 */4181,
|
|
/* 18 */6765, /* 19 */10946, /* 20 */17711, /* 21 */28657, /* 22 */46368,
|
|
/* 23 */75025, /* 24 */121393, /* 25 */196418, /* 26 */317811,
|
|
/* 27 */514229, /* 28 */832040, /* 29 */1346269, /* 30 */2178309,
|
|
/* 31 */3524578, /* 32 */5702887, /* 33 */9227465, /* 34 */14930352,
|
|
/* 35 */24157817, /* 36 */39088169, /* 37 */63245986, /* 38 */102334155,
|
|
/* 39 */165580141, /* 40 */267914296, /* 41 */433494437,
|
|
/* 42 */701408733, /* 43 */1134903170, /* 44 */1836311903,
|
|
/* 45 */2971215073 };
|
|
// These are Fibonacci numbers < 2**32.
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT,Alloc>::RopeBase *
|
|
rope<charT,Alloc>::balance(RopeBase *r)
|
|
{
|
|
RopeBase * forest[RopeBase::max_rope_depth + 1];
|
|
RopeBase * result = 0;
|
|
int i;
|
|
// Inariant:
|
|
// The concatenation of forest in descending order is equal to r.
|
|
// forest[i].size >= min_len[i]
|
|
// forest[i].depth = i
|
|
// References from forest are included in refcount.
|
|
|
|
for (i = 0; i <= RopeBase::max_rope_depth; ++i) forest[i] = 0;
|
|
__STL_TRY
|
|
add_to_forest(r, forest);
|
|
for (i = 0; i <= RopeBase::max_rope_depth; ++i) if (0 != forest[i]) {
|
|
# ifndef __GC
|
|
self_destruct_ptr old(result);
|
|
# endif
|
|
result = concat(forest[i], result);
|
|
forest[i] -> unref_nonnil();
|
|
}
|
|
__STL_UNWIND(for(i = 0; i <= RopeBase::max_rope_depth; i++)
|
|
unref(forest[i]))
|
|
if (result -> depth > RopeBase::max_rope_depth) abort();
|
|
return(result);
|
|
}
|
|
|
|
|
|
template <class charT, class Alloc>
|
|
void
|
|
rope<charT,Alloc>::add_to_forest(RopeBase *r, RopeBase **forest)
|
|
{
|
|
if (r -> is_balanced) {
|
|
add_leaf_to_forest(r, forest);
|
|
return;
|
|
}
|
|
__stl_assert(r -> tag == RopeBase::concat);
|
|
{
|
|
RopeConcatenation *c = (RopeConcatenation *)r;
|
|
|
|
add_to_forest(c -> left, forest);
|
|
add_to_forest(c -> right, forest);
|
|
}
|
|
}
|
|
|
|
|
|
template <class charT, class Alloc>
|
|
void
|
|
rope<charT,Alloc>::add_leaf_to_forest(RopeBase *r, RopeBase **forest)
|
|
{
|
|
self_destruct_ptr insertee(r); // included in refcount
|
|
self_destruct_ptr too_tiny(0); // included in refcount
|
|
int i; // forest[0..i-1] is empty
|
|
size_t s = insertee -> size;
|
|
|
|
ref(r);
|
|
for (i = 0; s >= min_len[i+1]/* not this bucket */; ++i) {
|
|
if (0 != forest[i]) {
|
|
# ifndef __GC
|
|
self_destruct_ptr old(too_tiny);
|
|
# endif
|
|
too_tiny = concat_and_set_balanced(forest[i], too_tiny);
|
|
forest[i] -> unref_nonnil();
|
|
forest[i] = 0;
|
|
}
|
|
}
|
|
{
|
|
# ifndef __GC
|
|
self_destruct_ptr old(insertee);
|
|
# endif
|
|
insertee = concat_and_set_balanced(too_tiny, insertee);
|
|
}
|
|
unref(too_tiny); // too_tiny is dead.
|
|
too_tiny = 0; // Needed for exception safety.
|
|
__stl_assert(is_almost_balanced(insertee));
|
|
__stl_assert(insertee -> depth <= r -> depth + 1);
|
|
for (;; ++i) {
|
|
if (0 != forest[i]) {
|
|
# ifndef __GC
|
|
self_destruct_ptr old(insertee);
|
|
# endif
|
|
insertee = concat_and_set_balanced(forest[i], insertee);
|
|
forest[i] -> unref_nonnil();
|
|
forest[i] = 0;
|
|
__stl_assert(is_almost_balanced(insertee));
|
|
}
|
|
__stl_assert(min_len[i] <= insertee -> size);
|
|
__stl_assert(forest[i] == 0);
|
|
if (i == RopeBase::max_rope_depth
|
|
|| insertee -> size < min_len[i+1]) {
|
|
forest[i] = insertee;
|
|
insertee = 0;
|
|
// refcount is OK since insertee is now dead.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
charT
|
|
rope<charT,Alloc>::fetch(RopeBase *r, size_type i)
|
|
{
|
|
__GC_CONST charT * cstr = r -> c_string;
|
|
|
|
__stl_assert(i < r -> size);
|
|
if (0 != cstr) return cstr[i];
|
|
for(;;) {
|
|
switch(r -> tag) {
|
|
case RopeBase::concat:
|
|
{
|
|
RopeConcatenation *c = (RopeConcatenation *)r;
|
|
RopeBase *left = c -> left;
|
|
size_t left_len = left -> size;
|
|
|
|
if (i >= left_len) {
|
|
i -= left_len;
|
|
r = c -> right;
|
|
} else {
|
|
r = left;
|
|
}
|
|
}
|
|
break;
|
|
case RopeBase::leaf:
|
|
{
|
|
RopeLeaf * l = (RopeLeaf *)r;
|
|
return l -> data[i];
|
|
}
|
|
case RopeBase::function:
|
|
case RopeBase::substringfn:
|
|
{
|
|
RopeFunction * f = (RopeFunction *)r;
|
|
charT result;
|
|
|
|
(*(f -> fn))(i, 1, &result);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# ifndef __GC
|
|
// Return a uniquely referenced character slot for the given
|
|
// position, or 0 if that's not possible.
|
|
template <class charT, class Alloc>
|
|
charT*
|
|
rope<charT,Alloc>::fetch_ptr(RopeBase *r, size_type i)
|
|
{
|
|
RopeBase * clrstack[RopeBase::max_rope_depth];
|
|
size_t csptr = 0;
|
|
|
|
for(;;) {
|
|
if (r -> refcount > 1) return 0;
|
|
switch(r -> tag) {
|
|
case RopeBase::concat:
|
|
{
|
|
RopeConcatenation *c = (RopeConcatenation *)r;
|
|
RopeBase *left = c -> left;
|
|
size_t left_len = left -> size;
|
|
|
|
if (c -> c_string != 0) clrstack[csptr++] = c;
|
|
if (i >= left_len) {
|
|
i -= left_len;
|
|
r = c -> right;
|
|
} else {
|
|
r = left;
|
|
}
|
|
}
|
|
break;
|
|
case RopeBase::leaf:
|
|
{
|
|
RopeLeaf * l = (RopeLeaf *)r;
|
|
if (l -> c_string != l -> data && l -> c_string != 0)
|
|
clrstack[csptr++] = l;
|
|
while (csptr > 0) {
|
|
-- csptr;
|
|
RopeBase * d = clrstack[csptr];
|
|
d -> free_c_string();
|
|
d -> c_string = 0;
|
|
}
|
|
return l -> data + i;
|
|
}
|
|
case RopeBase::function:
|
|
case RopeBase::substringfn:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
# endif /* __GC */
|
|
|
|
// The following could be implemented trivially using
|
|
// lexicographical_compare_3way.
|
|
// We do a little more work to avoid dealing with rope iterators for
|
|
// flat strings.
|
|
template <class charT, class Alloc>
|
|
int
|
|
rope<charT,Alloc>::compare (const RopeBase *left, const RopeBase *right)
|
|
{
|
|
size_t left_len;
|
|
size_t right_len;
|
|
|
|
if (0 == right) return 0 != left;
|
|
if (0 == left) return -1;
|
|
left_len = left -> size;
|
|
right_len = right -> size;
|
|
if (RopeBase::leaf == left -> tag) {
|
|
RopeLeaf *l = (RopeLeaf *) left;
|
|
if (RopeBase::leaf == right -> tag) {
|
|
RopeLeaf *r = (RopeLeaf *) right;
|
|
return lexicographical_compare_3way(
|
|
l -> data, l -> data + left_len,
|
|
r -> data, r -> data + right_len);
|
|
} else {
|
|
const_iterator rstart(right, 0);
|
|
const_iterator rend(right, right_len);
|
|
return lexicographical_compare_3way(
|
|
l -> data, l -> data + left_len,
|
|
rstart, rend);
|
|
}
|
|
} else {
|
|
const_iterator lstart(left, 0);
|
|
const_iterator lend(left, left_len);
|
|
if (RopeBase::leaf == right -> tag) {
|
|
RopeLeaf *r = (RopeLeaf *) right;
|
|
return lexicographical_compare_3way(
|
|
lstart, lend,
|
|
r -> data, r -> data + right_len);
|
|
} else {
|
|
const_iterator rstart(right, 0);
|
|
const_iterator rend(right, right_len);
|
|
return lexicographical_compare_3way(
|
|
lstart, lend,
|
|
rstart, rend);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assignment to reference proxies.
|
|
template <class charT, class Alloc>
|
|
__rope_charT_ref_proxy<charT, Alloc>&
|
|
__rope_charT_ref_proxy<charT, Alloc>::operator= (charT c) {
|
|
RopeBase * old = root -> tree_ptr;
|
|
# ifndef __GC
|
|
// First check for the case in which everything is uniquely
|
|
// referenced. In that case we can do this destructively.
|
|
charT * charT_ptr = my_rope::fetch_ptr(old, pos);
|
|
if (0 != charT_ptr) {
|
|
*charT_ptr = c;
|
|
return *this;
|
|
}
|
|
# endif
|
|
self_destruct_ptr left(my_rope::substring(old, 0, pos));
|
|
self_destruct_ptr right(my_rope::substring(old, pos+1, old -> size));
|
|
self_destruct_ptr result_left(my_rope::destr_concat_char_iter(left, &c, 1));
|
|
# ifndef __GC
|
|
__stl_assert(left == result_left || 1 == result_left -> refcount);
|
|
# endif
|
|
RopeBase * result =
|
|
my_rope::concat(result_left, right);
|
|
# ifndef __GC
|
|
__stl_assert(1 <= result -> refcount);
|
|
RopeBase::unref(old);
|
|
# endif
|
|
root -> tree_ptr = result;
|
|
return *this;
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
inline __rope_charT_ref_proxy<charT, Alloc>::operator charT () const
|
|
{
|
|
if (current_valid) {
|
|
return current;
|
|
} else {
|
|
return my_rope::fetch(root->tree_ptr, pos);
|
|
}
|
|
}
|
|
template <class charT, class Alloc>
|
|
__rope_charT_ptr_proxy<charT, Alloc>
|
|
__rope_charT_ref_proxy<charT, Alloc>::operator& () const {
|
|
return __rope_charT_ptr_proxy<charT, Alloc>(*this);
|
|
}
|
|
|
|
template <class charT, class Alloc>
|
|
rope<charT, Alloc>::rope(size_t n, charT c)
|
|
{
|
|
RopeBase * result;
|
|
const size_t exponentiate_threshold = 32;
|
|
size_t exponent;
|
|
size_t rest;
|
|
charT *rest_buffer;
|
|
RopeBase * remainder;
|
|
|
|
if (0 == n) { tree_ptr = 0; return; }
|
|
exponent = n / exponentiate_threshold;
|
|
rest = n % exponentiate_threshold;
|
|
if (0 == rest) {
|
|
remainder = 0;
|
|
} else {
|
|
rest_buffer = (charT *)Alloc::allocate(rounded_up_size(rest));
|
|
uninitialized_fill_n(rest_buffer, rest, c);
|
|
__cond_store_eos(rest_buffer[rest]);
|
|
__STL_TRY
|
|
remainder = RopeLeaf_from_char_ptr(rest_buffer, rest);
|
|
__STL_UNWIND(RopeBase::free_string(rest_buffer, rest))
|
|
}
|
|
__STL_TRY
|
|
if (exponent != 0) {
|
|
charT * base_buffer =
|
|
(charT *)Alloc::allocate(
|
|
rounded_up_size(exponentiate_threshold));
|
|
self_destruct_ptr base_leaf;
|
|
uninitialized_fill_n(base_buffer, exponentiate_threshold, c);
|
|
__cond_store_eos(base_buffer[exponentiate_threshold]);
|
|
__STL_TRY
|
|
base_leaf = RopeLeaf_from_char_ptr(base_buffer,
|
|
exponentiate_threshold);
|
|
__STL_UNWIND(RopeBase::free_string(base_buffer, exponentiate_threshold))
|
|
if (1 == exponent) {
|
|
result = base_leaf;
|
|
# ifndef __GC
|
|
__stl_assert(1 == result -> refcount);
|
|
result -> refcount = 2; // will be decremented when base_leaf disappears
|
|
# endif
|
|
} else {
|
|
result = power((RopeBase *)base_leaf, exponent, concat_fn());
|
|
# ifndef __GC
|
|
__stl_assert(0 == result -> refcount);
|
|
result -> refcount = 1;
|
|
# endif
|
|
}
|
|
if (0 != remainder) {
|
|
# ifndef __GC
|
|
__stl_assert(1 == remainder -> refcount);
|
|
# endif
|
|
result = tree_concat(result, remainder);
|
|
}
|
|
// All partial results computed by power must be used.
|
|
} else {
|
|
result = remainder;
|
|
}
|
|
__STL_UNWIND(unref(remainder));
|
|
# ifndef __GC
|
|
__stl_assert(0 == result || 1 == result -> refcount);
|
|
# endif
|
|
tree_ptr = result;
|
|
}
|
|
|
|
template<class charT, class Alloc> charT rope<charT,Alloc>::empty_c_str[1];
|
|
|
|
# ifdef _PTHREADS
|
|
template<class charT, class Alloc>
|
|
pthread_mutex_t rope<charT,Alloc>::swap_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
# endif
|
|
|
|
template<class charT, class Alloc>
|
|
const charT * rope<charT,Alloc>::c_str() const {
|
|
if (0 == tree_ptr) {
|
|
empty_c_str[0] = __eos((charT *)0); // Possibly redundant,
|
|
// but probably fast.
|
|
return empty_c_str;
|
|
}
|
|
__GC_CONST charT * old_c_string = tree_ptr -> c_string;
|
|
if (0 != old_c_string) return(old_c_string);
|
|
size_t s = size();
|
|
charT * result = (charT *)Alloc::allocate((s + 1)*sizeof(charT));
|
|
flatten(tree_ptr, result);
|
|
result[s] = __eos((charT *)0);
|
|
# ifdef __GC
|
|
tree_ptr -> c_string = result;
|
|
# else
|
|
if ((old_c_string = atomic_swap(&(tree_ptr -> c_string), result)) != 0) {
|
|
// It must have been added in the interim. Hence it had to have been
|
|
// separately allocated. Deallocate the old copy, since we just
|
|
// replaced it.
|
|
destroy(old_c_string, old_c_string + s + 1);
|
|
Alloc::deallocate(old_c_string, s + 1);
|
|
}
|
|
# endif
|
|
return(result);
|
|
}
|
|
|
|
template<class charT, class Alloc>
|
|
const charT * rope<charT,Alloc>::replace_with_c_str() {
|
|
if (0 == tree_ptr) {
|
|
empty_c_str[0] = __eos((charT *)0);
|
|
return empty_c_str;
|
|
}
|
|
__GC_CONST charT * old_c_string = tree_ptr -> c_string;
|
|
if (RopeBase::leaf == tree_ptr -> tag && 0 != old_c_string) {
|
|
return(old_c_string);
|
|
}
|
|
size_t s = size();
|
|
charT * result = (charT *)Alloc::allocate(rounded_up_size(s));
|
|
flatten(tree_ptr, result);
|
|
result[s] = __eos((charT *)0);
|
|
tree_ptr -> unref_nonnil();
|
|
tree_ptr = RopeLeaf_from_char_ptr(result, s);
|
|
return(result);
|
|
}
|
|
|
|
// Algorithm specializations. More should be added.
|
|
|
|
#ifndef _MSC_VER
|
|
// I couldn't get this to work with VC++
|
|
template<class charT,class Alloc>
|
|
void
|
|
__rope_rotate(__rope_iterator<charT,Alloc> first,
|
|
__rope_iterator<charT,Alloc> middle,
|
|
__rope_iterator<charT,Alloc> last) {
|
|
__stl_assert(first.container() == middle.container()
|
|
&& middle.container() == last.container());
|
|
rope<charT,Alloc>& r(first.container());
|
|
rope<charT,Alloc> prefix = r.substr(0, first.index());
|
|
rope<charT,Alloc> suffix = r.substr(last.index(), r.size() - last.index());
|
|
rope<charT,Alloc> part1 = r.substr(middle.index(),
|
|
last.index() - middle.index());
|
|
rope<charT,Alloc> part2 = r.substr(first.index(),
|
|
middle.index() - first.index());
|
|
r = prefix;
|
|
r += part1;
|
|
r += part2;
|
|
r += suffix;
|
|
}
|
|
|
|
inline void rotate(__rope_iterator<char,__ALLOC> first,
|
|
__rope_iterator<char,__ALLOC> middle,
|
|
__rope_iterator<char,__ALLOC> last) {
|
|
__rope_rotate(first, middle, last);
|
|
}
|
|
|
|
# if 0
|
|
// Probably not useful for several reasons:
|
|
// - for SGIs 7.1 compiler and probably some others,
|
|
// this forces lots of rope<wchar_t, ...> instantiations, creating a
|
|
// code bloat and compile time problem. (Fixed in 7.2.)
|
|
// - wchar_t is 4 bytes wide on most UNIX platforms, making it unattractive
|
|
// for unicode strings. Unsigned short may be a better character
|
|
// type.
|
|
inline void rotate(__rope_iterator<wchar_t,__ALLOC> first,
|
|
__rope_iterator<wchar_t,__ALLOC> middle,
|
|
__rope_iterator<wchar_t,__ALLOC> last) {
|
|
__rope_rotate(first, middle, last);
|
|
}
|
|
# endif
|
|
#endif /* _MSC_VER */
|