1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it if you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or as a part of
11 # Introduces ropes with buffered leaves
12 module bufferized_ropes
15 intrude import standard
::ropes
17 # Leaf containing a FlatBuffer to optimize concatenation operations
18 private class BufferLeaf
21 init(val
: FlatBuffer) do
32 if right
== null then return new StringLeaf("".as(FlatString))
35 if right
== null then return left
.as(not null).to_leaf
36 if left
.length
+ right
.length
< buf_len
then
37 var b
= new FlatBuffer.with_capacity
(buf_len
)
38 b
.append
(left
.to_leaf
.str
)
39 b
.append
(right
.to_leaf
.str
)
40 return new BufferLeaf(b
)
42 var b
= new FlatBuffer.with_capacity
(left
.length
+ right
.length
)
43 b
.append
(left
.to_leaf
.str
)
44 b
.append
(right
.to_leaf
.str
)
45 return new StringLeaf(b
.lazy_to_s
(b
.length
))
52 # Creates a substring, only without any copy overhead for Buffers
53 # The call to lazy_substring ensures the creation of a FlatString, which is required for Leaves.
54 private fun lazy_substring
(from
: Int, length
: Int): FlatString is abstract
56 # Same as substring_from, but without copy of the data for Buffers.
57 private fun lazy_substring_from
(from
: Int): FlatString is abstract
60 redef class FlatBuffer
62 # Same as to_s, only will not copy self before returning a String.
63 private fun lazy_to_s
(len
: Int): FlatString
65 return new FlatString.with_infos
(items
, len
, 0, length
- 1)
68 redef fun lazy_substring
(from
,length
)
70 return new FlatString.with_infos
(items
, length
, from
, from
+ length
- 1)
73 redef fun lazy_substring_from
(from
)
75 var newlen
= length
- from
76 return new FlatString.with_infos
(items
, newlen
, from
, from
+ newlen
- 1)
81 redef class FlatString
83 redef fun lazy_substring
(from
, len
) do return substring
(from
,len
).as(FlatString)
85 redef fun lazy_substring_from
(from
) do return substring_from
(from
).as(FlatString)
92 init do root
= new BufferLeaf(new FlatBuffer.with_capacity
(buf_len
))
96 redef class RopeString
98 init from
(s
: String) do
99 if s
.length
< buf_len
then
100 var b
= new FlatBuffer.with_capacity
(buf_len
)
102 root
= new BufferLeaf(b
)
104 if s
isa RopeString then root
= s
.root
else root
= new StringLeaf(s
.as(FlatString))
108 # Inserts a String `str` at position `pos`
109 redef fun insert_at
(str
, pos
)
111 if str
.length
== 0 then return self
113 assert pos
>= 0 and pos
<= length
115 if pos
== length
then
117 if r
isa BufferLeaf then
118 var b
= r
.str
.as(FlatBuffer)
119 if r
.length
+ str
.length
< b
.capacity
then
121 return new RopeString.from_root
(new BufferLeaf(b
))
126 var path
= node_at
(pos
)
130 if path
.offset
== path
.leaf
.length
then
131 cct
= build_node_len_offset
(path
, str
)
132 else if path
.offset
== 0 then
133 cct
= build_node_zero_offset
(path
, str
)
135 cct
= build_node_other
(path
,str
)
138 if path
.stack
.is_empty
then return new RopeString.from_root
(cct
)
140 var tmp
= path
.stack
.pop
141 var last_concat
: Concat
144 last_concat
= new Concat(cct
,tmp
.node
.right
.as(not null))
146 last_concat
= new Concat(tmp
.node
.left
.as(not null), cct
)
149 for i
in path
.stack
.reverse_iterator
do
152 nod
= new Concat(last_concat
, i
.node
.right
.as(not null))
154 nod
= new Concat(i
.node
.left
.as(not null), last_concat
)
159 return new RopeString.from_root
(last_concat
)
162 redef fun substring
(pos
, len
)
169 if pos
+ len
> length
then len
= length
- pos
171 if len
<= 0 then return new RopeString
173 var path
= node_at
(pos
)
176 var offset
= path
.offset
179 if lf
isa StringLeaf then
180 s
= lf
.str
.as(FlatString)
182 s
= lf
.str
.as(FlatBuffer).lazy_to_s
(lf
.length
)
185 if path
.leaf
.str
.length
- offset
> len
then
186 lf
= new StringLeaf(s
.substring
(offset
,len
).as(FlatString))
188 lf
= new StringLeaf(s
.substring_from
(offset
).as(FlatString))
191 var nod
: RopeNode = lf
193 if lf
.length
== len
then return new RopeString.from_root
(lf
)
195 var lft
: nullable RopeNode
196 var rht
: nullable RopeNode
198 for i
in path
.stack
.reverse_iterator
do
199 if i
.right
then continue
202 nod
= new Concat(lft
, rht
)
205 var ret
= new RopeString
208 path
= ret
.node_at
(len-1
)
214 if lf
isa StringLeaf then
215 s
= lf
.str
.as(FlatString)
217 s
= lf
.str
.as(FlatBuffer).lazy_to_s
(lf
.length
)
220 nod
= new StringLeaf(s
.substring
(0, offset
+1).as(FlatString))
222 for i
in path
.stack
.reverse_iterator
do
223 if i
.left
then continue
226 nod
= new Concat(lft
, rht
)
234 private fun build_node_zero_offset
(path
: Path, s
: String): RopeNode
236 var finlen
= path
.leaf
.length
+ s
.length
237 if finlen
<= buf_len
then
238 var b
= new FlatBuffer.with_capacity
(buf_len
)
240 b
.append
(path
.leaf
.str
)
241 if finlen
== buf_len
then return new StringLeaf(b
.lazy_to_s
(finlen
))
242 return new BufferLeaf(b
)
246 if s
isa FlatString then
247 if s
.length
> buf_len
then
248 lft
= new StringLeaf(s
)
250 var b
= new FlatBuffer
252 lft
= new BufferLeaf(b
)
255 lft
= s
.as(RopeString).root
257 return new Concat(lft
, rht
)
260 private fun build_node_len_offset
(path
: Path, s
: String): RopeNode
263 if leaf
isa BufferLeaf then
264 if s
.length
> buf_len
then
265 if s
isa FlatString then
266 return new Concat(leaf
, new StringLeaf(s
))
268 return new Concat(leaf
, s
.as(Rope).root
)
271 var finlen
= leaf
.length
+ s
.length
272 var buf
= leaf
.str
.as(FlatBuffer)
273 var cap
= buf
.capacity
274 # Meaning the buffer was modified elsewhere
275 # Therefore, we create a new one
276 if leaf
.length
!= buf
.length
then
277 var b
= new FlatBuffer.with_capacity
(buf_len
)
278 b
.append
(buf
.lazy_to_s
(leaf
.length
))
281 if finlen
<= cap
then
283 if finlen
== buf_len
then return new StringLeaf(buf
.lazy_to_s
(finlen
))
284 return new BufferLeaf(buf
)
286 var l_len
= finlen
- cap
287 buf
.append
(s
.substring
(0,l_len
))
288 var b2
= new FlatBuffer.with_capacity
(buf_len
)
289 b2
.append
(s
.substring_from
(l_len
))
290 var left_leaf
= new StringLeaf(buf
.lazy_to_s
(buf
.length
))
291 var right_leaf
= new BufferLeaf(b2
)
292 var cct
= new Concat(left_leaf
, right_leaf
)
298 if s
.length
>= buf_len
then
299 if s
isa FlatString then rht
= new StringLeaf(s
) else rht
= s
.as(Rope).root
301 var buf
= new FlatBuffer.with_capacity
(buf_len
)
303 rht
= new BufferLeaf(buf
)
305 return new Concat(lft
,rht
)
309 private fun build_node_other
(path
: Path,str
: String): RopeNode
313 if lf
isa BufferLeaf then
314 var b
= lf
.str
.as(FlatBuffer)
315 s
= b
.lazy_to_s
(lf
.length
)
317 s
= lf
.str
.as(FlatString)
319 var l_str
= s
.lazy_substring
(0, path
.offset
)
320 var r_str
= s
.lazy_substring_from
(path
.offset
)
321 if s
.length
+ str
.length
< buf_len
then
322 var buf
= new FlatBuffer.with_capacity
(buf_len
)
326 return new BufferLeaf(buf
)
329 if str
isa FlatString then child
= new StringLeaf(str
) else child
= str
.as(Rope).root
330 var l_cct
= new Concat(new StringLeaf(l_str
), child
)
331 var r_cct
= new Concat(l_cct
, new StringLeaf(r_str
))
337 redef class SubstringsIterator
339 # Compute the bounds of the current substring and makes the substring
340 redef fun make_substring
345 var length
= l
.length
346 if nodes
.index
< pos
then
347 min
= pos
- nodes
.index
349 substring
= s
.lazy_substring
(min
, length
)
354 redef class ReverseSubstringsIterator
356 redef fun make_substring
360 if pos
> (leaves
.index
+ l
.length
- 1) then return
361 str
= s
.lazy_substring
(0, (pos
- leaves
.index
+ 1))
366 # Default size of a buffer in a rope leaf.
367 fun buf_len
: Int do return 200