a2a80238c76c72f552c2b5f369d2355847cc726d
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 is 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 is a part of
11 # Ropes with a special kind of Leaves that act similar to a `Buffer`
13 # When using this module, re-allocations are limited by the introduction
14 # of a larger-than-necessary buffered area for the native part of a `String`
15 # in an append-only fashion.
17 # Concretely, when concatenating two small strings of length `n` + `m` < `maxlen`
18 # What happens is that a `maxlen` byte buffer is allocated, ready to receive more
19 # bytes a posteriori without necessarily reallocating a new byte array.
21 # Theoretically, this should lower the number of concatenations
22 # and reallocations when concatenating `String` objects.
25 intrude import standard
::text
::ropes
27 # Hidden buffer, used to simulate a `FlatBuffer` on a short string.
29 # This is to be used by low-level APIs because of its lack of
30 # safety, if you use it, make sure you know what you are doing !
32 # Practically, it is the underlying representation of a `Leaf` in
33 # the `Rope` block, its advantage is that it saves a bit more space
34 # for future concatenations, without risking to overwrite previously
35 # used space, making it suitable for Strings.
37 # Note for future use : Should there be parallel capacity in Nit at
38 # some point, this is NOT thread safe !
39 private class ManualBuffer
40 var ns
: NativeString is noinit
41 # Current position in the `NativeString`
43 # It is used by the clients of `ManualBuffer` as a guard
44 # to detect if the concatenation in the `ManualBuffer`
48 # Say we have two strings `x` and `y` referencing the
49 # same `ManualBuffer` `b`, `y` is the concatenation of
50 # `x` and another string.
52 # If we try to concatenate a `String` `z` to `x`, a new
53 # `ManualBuffer` will be created since `pos` and `x.length`
56 # However, if we concatenate the same `String` to `y`,
57 # the contents of `z` will be copied to the `ManualBuffer`.
60 init do ns
= new NativeString(maxlen
)
62 fun [](i
: Int): Byte do return ns
[i
]
65 # Simple implementation of the iterator on Substrings for `Leaf`
67 # Basically just returns `self` encapsulated in a `FlatString`.
68 private class LeafSubstrings
69 super IndexedIterator[FlatText]
72 var str
: FlatString is noinit
76 str
= new FlatString.with_infos
(leaf
.buf
.ns
, leaf
.length
, 0, leaf
.length
- 1)
79 redef fun is_ok
do return avail
81 redef fun next
do avail
= false
83 redef fun index
do return 0
85 redef fun item
do return str
88 # Leaf of a `Rope`, used as a buffered area for speedy concatenation.
94 var bns
: NativeString is noinit
95 redef var length
is noinit
97 redef fun empty
do return new Leaf(new ManualBuffer)
99 redef fun to_cstring
do
101 var ns
= new NativeString(len
+ 1)
103 buf
.ns
.copy_to
(ns
, len
, 0, 0)
107 redef fun substrings
do return new LeafSubstrings(self)
109 redef fun [](i
) do return buf
[i
].to_i
.ascii
116 redef fun output
do new FlatString.with_infos
(buf
.ns
, length
, 0, length
- 1).output
118 redef fun to_upper
do
119 var x
= new FlatBuffer
120 for i
in chars
do x
.add
(i
.to_upper
)
124 redef fun to_lower
do
125 var x
= new FlatBuffer
126 for i
in chars
do x
.add
(i
.to_lower
)
130 redef fun reversed
do
131 var x
= new ManualBuffer
136 for i
in [0 .. mlen
[ do
144 redef fun substring
(from
, len
) do
145 return new FlatString.with_infos
(buf
.ns
, len
, from
, from
+ len
- 1)
148 redef fun insert_at
(s
, pos
) do
149 var l
= substring
(0, pos
)
150 var r
= substring_from
(pos
)
158 if slen
== 0 then return self
159 if mlen
== 0 then return s
160 var nlen
= mlen
+ slen
161 if nlen
> maxlen
then return new Concat(self, s
)
162 if s
isa FlatString then
166 sits
.copy_to
(buf
.ns
, slen
, s
.index_from
, bpos
)
167 buf
.pos
= bpos
+ slen
170 var b
= new ManualBuffer
172 bns
.copy_to
(nbns
, mlen
, 0, 0)
173 sits
.copy_to
(nbns
, slen
, s
.index_from
, mlen
)
177 else if s
isa Leaf then
181 sbns
.copy_to
(bns
, slen
, 0, bpos
)
185 var b
= new ManualBuffer
187 bns
.copy_to
(nbns
, mlen
, 0, 0)
188 sbns
.copy_to
(nbns
, slen
, 0, mlen
)
192 else if s
isa Concat then
193 if not s
.left
isa Concat then
194 return new Concat(self + s
.left
, s
.right
)
196 return new Concat(self, s
)
202 bns
.copy_to
(b
.ns
, mlen
, 0, 0)
214 redef fun to_cstring
do
216 var ns
= new NativeString(len
+ 1)
219 for i
in substrings
do
221 if i
isa FlatString then
222 i
.items
.copy_to
(ns
, ilen
, i
.index_from
, off
)
223 else if i
isa Leaf then
224 i
.buf
.ns
.copy_to
(ns
, ilen
, 0, off
)
236 if s
isa FlatString then
239 if rlen
+ slen
> maxlen
then return new Concat(left
, new Concat(r
, s
))
240 return new Concat(left
, r
+ s
)
241 else if s
isa Concat then
242 return new Concat(self, s
)
243 else if s
isa Leaf then
246 if rlen
+ slen
> maxlen
then return new Concat(left
, new Concat(r
, s
))
247 return new Concat(left
, r
+ s
)
254 redef class FlatString
259 if slen
== 0 then return self
260 if mlen
== 0 then return s
261 if s
isa FlatString then
262 if slen
+ mlen
> maxlen
then return new Concat(self, s
)
264 var sifrom
= s
.index_from
265 var mifrom
= index_from
267 var b
= new ManualBuffer
269 mits
.copy_to
(bns
, mlen
, mifrom
, 0)
270 sits
.copy_to
(bns
, slen
, sifrom
, mlen
)
273 else if s
isa Concat then
275 var sllen
= sl
.length
276 if sllen
+ mlen
> maxlen
then return new Concat(self, s
)
277 return new Concat(sl
+ self, s
.right
)
278 else if s
isa Leaf then
279 if slen
+ mlen
> maxlen
then return new Concat(self, s
)
280 var mifrom
= index_from
282 var b
= new ManualBuffer
284 items
.copy_to
(bns
, mlen
, mifrom
, 0)
285 sb
.ns
.copy_to
(bns
, slen
, 0, mlen
)
296 # Fast implementation
299 if l
== 0 then return ""
300 if l
== 1 then if self[0] == null then return "" else return self[0].to_s
302 var na
= new NativeArray[String](l
)
318 var ns
= new NativeString(sl
+ 1)
325 if tmp
isa FlatString then
326 tmp
.items
.copy_to
(ns
, tpl
, tmp
.index_from
, off
)
329 for j
in tmp
.substrings
do
331 if j
isa FlatString then
332 j
.items
.copy_to
(ns
, slen
, j
.index_from
, off
)
333 else if j
isa Leaf then
334 j
.buf
.ns
.copy_to
(ns
, slen
, 0, off
)
341 return ns
.to_s_with_length
(sl
)