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 # All the array-based text representations
14 intrude import abstract_text
21 private class FlatSubstringsIter
22 super Iterator[FlatText]
24 var tgt
: nullable FlatText
28 return tgt
.as(not null)
31 redef fun is_ok
do return tgt
!= null
33 redef fun next
do tgt
= null
36 # Immutable strings of characters.
41 # Index in _items of the start of the string
42 private var index_from
: Int is noinit
44 # Indes in _items of the last item of the string
45 private var index_to
: Int is noinit
47 redef var chars
= new FlatStringCharView(self) is lazy
51 # Check that the index (+ index_from) is not larger than indexTo
52 # In other terms, if the index is valid
54 assert (index
+ index_from
) <= index_to
55 return items
[index
+ index_from
]
58 ################################################
59 # AbstractString specific methods #
60 ################################################
64 var native
= new NativeString(self.length
+ 1)
65 var length
= self.length
66 var items
= self.items
70 native
[pos
] = items
[ipos
]
74 return native
.to_s_with_length
(self.length
)
77 redef fun fast_cstring
do return items
.fast_cstring
(index_from
)
79 redef fun substring
(from
, count
)
85 if count
< 0 then count
= 0
89 var new_from
= index_from
+ from
91 if (new_from
+ count
) > index_to
then
92 var new_len
= index_to
- new_from
+ 1
93 if new_len
<= 0 then return empty
94 return new FlatString.with_infos
(items
, new_len
, new_from
, index_to
)
97 if count
<= 0 then return empty
99 var to
= new_from
+ count
- 1
101 return new FlatString.with_infos
(items
, to
- new_from
+ 1, new_from
, to
)
104 redef fun empty
do return "".as(FlatString)
108 var outstr
= new NativeString(self.length
+ 1)
111 var myitems
= self.items
112 var index_from
= self.index_from
113 var max
= self.index_to
115 while index_from
<= max
do
116 outstr
[out_index
] = myitems
[index_from
].to_upper
121 outstr
[self.length
] = '\0'
123 return outstr
.to_s_with_length
(self.length
)
128 var outstr
= new NativeString(self.length
+ 1)
131 var myitems
= self.items
132 var index_from
= self.index_from
133 var max
= self.index_to
135 while index_from
<= max
do
136 outstr
[out_index
] = myitems
[index_from
].to_lower
141 outstr
[self.length
] = '\0'
143 return outstr
.to_s_with_length
(self.length
)
148 var i
= self.index_from
149 var imax
= self.index_to
156 ##################################################
157 # String Specific Methods #
158 ##################################################
160 # Low-level creation of a new string with given data.
162 # `items` will be used as is, without copy, to retrieve the characters of the string.
163 # Aliasing issues is the responsibility of the caller.
164 private init with_infos
(items
: NativeString, length
: Int, from
: Int, to
: Int)
174 if real_items
!= null then
175 return real_items
.as(not null)
177 var newItems
= new NativeString(length
+ 1)
178 self.items
.copy_to
(newItems
, length
, index_from
, 0)
179 newItems
[length
] = '\0'
180 self.real_items
= newItems
187 if not other
isa FlatString then return super
189 if self.object_id
== other
.object_id
then return true
191 var my_length
= length
193 if other
.length
!= my_length
then return false
195 var my_index
= index_from
196 var its_index
= other
.index_from
198 var last_iteration
= my_index
+ my_length
200 var itsitems
= other
.items
201 var myitems
= self.items
203 while my_index
< last_iteration
do
204 if myitems
[my_index
] != itsitems
[its_index
] then return false
214 if not other
isa FlatString then return super
216 if self.object_id
== other
.object_id
then return false
218 var my_curr_char
: Char
219 var its_curr_char
: Char
221 var curr_id_self
= self.index_from
222 var curr_id_other
= other
.index_from
224 var my_items
= self.items
225 var its_items
= other
.items
227 var my_length
= self.length
228 var its_length
= other
.length
230 var max_iterations
= curr_id_self
+ my_length
232 while curr_id_self
< max_iterations
do
233 my_curr_char
= my_items
[curr_id_self
]
234 its_curr_char
= its_items
[curr_id_other
]
236 if my_curr_char
!= its_curr_char
then
237 if my_curr_char
< its_curr_char
then return true
245 return my_length
< its_length
250 var my_length
= self.length
251 var its_length
= s
.length
253 var total_length
= my_length
+ its_length
255 var target_string
= new NativeString(my_length
+ its_length
+ 1)
257 self.items
.copy_to
(target_string
, my_length
, index_from
, 0)
258 if s
isa FlatString then
259 s
.items
.copy_to
(target_string
, its_length
, s
.index_from
, my_length
)
260 else if s
isa FlatBuffer then
261 s
.items
.copy_to
(target_string
, its_length
, 0, my_length
)
263 var curr_pos
= my_length
264 for i
in [0..s
.length
[ do
266 target_string
[curr_pos
] = c
271 target_string
[total_length
] = '\0'
273 return target_string
.to_s_with_length
(total_length
)
280 var my_length
= self.length
282 var final_length
= my_length
* i
284 var my_items
= self.items
286 var target_string
= new NativeString(final_length
+ 1)
288 target_string
[final_length
] = '\0'
292 for iteration
in [1 .. i
] do
293 my_items
.copy_to
(target_string
, my_length
, 0, current_last
)
294 current_last
+= my_length
297 return target_string
.to_s_with_length
(final_length
)
302 if hash_cache
== null then
303 # djb2 hash algorithm
309 while i
<= index_to
do
310 h
= h
.lshift
(5) + h
+ myitems
[i
].ascii
317 return hash_cache
.as(not null)
320 redef fun substrings
do return new FlatSubstringsIter(self)
323 private class FlatStringReverseIterator
324 super IndexedIterator[Char]
326 var target
: FlatString
328 var target_items
: NativeString
332 init with_pos
(tgt
: FlatString, pos
: Int)
335 target_items
= tgt
.items
336 curr_pos
= pos
+ tgt
.index_from
339 redef fun is_ok
do return curr_pos
>= target
.index_from
341 redef fun item
do return target_items
[curr_pos
]
343 redef fun next
do curr_pos
-= 1
345 redef fun index
do return curr_pos
- target
.index_from
349 private class FlatStringIterator
350 super IndexedIterator[Char]
352 var target
: FlatString
354 var target_items
: NativeString
358 init with_pos
(tgt
: FlatString, pos
: Int)
361 target_items
= tgt
.items
362 curr_pos
= pos
+ target
.index_from
365 redef fun is_ok
do return curr_pos
<= target
.index_to
367 redef fun item
do return target_items
[curr_pos
]
369 redef fun next
do curr_pos
+= 1
371 redef fun index
do return curr_pos
- target
.index_from
375 private class FlatStringCharView
378 redef type SELFTYPE: FlatString
382 # Check that the index (+ index_from) is not larger than indexTo
383 # In other terms, if the index is valid
385 var target
= self.target
386 assert (index
+ target
.index_from
) <= target
.index_to
387 return target
.items
[index
+ target
.index_from
]
390 redef fun iterator_from
(start
) do return new FlatStringIterator.with_pos
(target
, start
)
392 redef fun reverse_iterator_from
(start
) do return new FlatStringReverseIterator.with_pos
(target
, start
)
397 redef new do return new FlatBuffer
399 redef new with_cap
(i
) do return new FlatBuffer.with_capacity
(i
)
402 # Mutable strings of characters.
407 redef var chars
: Sequence[Char] = new FlatBufferCharView(self) is lazy
409 private var capacity
: Int = 0
411 redef fun fast_cstring
do return items
.fast_cstring
(0)
413 redef fun substrings
do return new FlatSubstringsIter(self)
415 # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
417 # This happens when an operation modifies the current `Buffer` and
418 # the Copy-On-Write flag `written` is set at true.
420 var nns
= new NativeString(capacity
)
421 items
.copy_to
(nns
, length
, 0, 0)
429 assert index
< length
433 redef fun []=(index
, item
)
436 if index
== length
then
440 if written
then reset
441 assert index
>= 0 and index
< length
448 if capacity
<= length
then enlarge
(length
+ 5)
455 if written
then reset
459 redef fun empty
do return new Buffer
461 redef fun enlarge
(cap
)
464 if cap
<= c
then return
465 while c
<= cap
do c
= c
* 2 + 2
466 # The COW flag can be set at false here, since
467 # it does a copy of the current `Buffer`
469 var a
= new NativeString(c
+1)
470 if length
> 0 then items
.copy_to
(a
, length
, 0, 0)
478 if length
== 0 then items
= new NativeString(1)
479 return new FlatString.with_infos
(items
, length
, 0, length
- 1)
485 var new_native
= new NativeString(length
+ 1)
486 new_native
[length
] = '\0'
487 if length
> 0 then items
.copy_to
(new_native
, length
, 0, 0)
488 real_items
= new_native
491 return real_items
.as(not null)
494 # Create a new empty string.
497 # Low-level creation a new buffer with given data.
499 # `items` will be used as is, without copy, to store the characters of the buffer.
500 # Aliasing issues is the responsibility of the caller.
502 # If `items` is shared, `written` should be set to true after the creation
503 # so that a modification will do a copy-on-write.
504 private init with_infos
(items
: NativeString, capacity
, length
: Int)
508 self.capacity
= capacity
511 # Create a new string copied from `s`.
514 capacity
= s
.length
+ 1
516 items
= new NativeString(capacity
)
517 if s
isa FlatString then
518 s
.items
.copy_to
(items
, length
, s
.index_from
, 0)
519 else if s
isa FlatBuffer then
520 s
.items
.copy_to
(items
, length
, 0, 0)
523 for i
in [0..s
.length
[ do
531 # Create a new empty string with a given capacity.
532 init with_capacity
(cap
: Int)
535 items
= new NativeString(cap
+1)
542 if s
.is_empty
then return
545 if capacity
< length
+ sl
then enlarge
(length
+ sl
)
546 if s
isa FlatString then
547 s
.items
.copy_to
(items
, sl
, s
.index_from
, length
)
548 else if s
isa FlatBuffer then
549 s
.items
.copy_to
(items
, sl
, 0, length
)
551 var curr_pos
= self.length
552 for i
in [0..s
.length
[ do
561 # Copies the content of self in `dest`
562 fun copy
(start
: Int, len
: Int, dest
: Buffer, new_start
: Int)
564 var self_chars
= self.chars
565 var dest_chars
= dest
.chars
566 for i
in [0..len-1
] do
567 dest_chars
[new_start
+i
] = self_chars
[start
+i
]
571 redef fun substring
(from
, count
)
575 if from
< 0 then from
= 0
576 if count
> length
then count
= length
578 var len
= count
- from
579 var r_items
= new NativeString(len
)
580 items
.copy_to
(r_items
, len
, from
, 0)
581 var r
= new FlatBuffer.with_infos
(r_items
, len
, len
)
591 var ns
= new NativeString(capacity
)
603 redef fun times
(repeats
)
605 var x
= new FlatString.with_infos
(items
, length
, 0, length
- 1)
606 for i
in [1..repeats
[ do
613 if written
then reset
617 it
[id
] = it
[id
].to_upper
624 if written
then reset
628 it
[id
] = it
[id
].to_lower
634 private class FlatBufferReverseIterator
635 super IndexedIterator[Char]
637 var target
: FlatBuffer
639 var target_items
: NativeString
643 init with_pos
(tgt
: FlatBuffer, pos
: Int)
646 if tgt
.length
> 0 then target_items
= tgt
.items
650 redef fun index
do return curr_pos
652 redef fun is_ok
do return curr_pos
>= 0
654 redef fun item
do return target_items
[curr_pos
]
656 redef fun next
do curr_pos
-= 1
660 private class FlatBufferCharView
663 redef type SELFTYPE: FlatBuffer
665 redef fun [](index
) do return target
.items
[index
]
667 redef fun []=(index
, item
)
669 assert index
>= 0 and index
<= length
670 if index
== length
then
674 target
.items
[index
] = item
687 fun enlarge
(cap
: Int)
694 var s_length
= s
.length
695 if target
.capacity
< s
.length
then enlarge
(s_length
+ target
.length
)
698 redef fun iterator_from
(pos
) do return new FlatBufferIterator.with_pos
(target
, pos
)
700 redef fun reverse_iterator_from
(pos
) do return new FlatBufferReverseIterator.with_pos
(target
, pos
)
704 private class FlatBufferIterator
705 super IndexedIterator[Char]
707 var target
: FlatBuffer
709 var target_items
: NativeString
713 init with_pos
(tgt
: FlatBuffer, pos
: Int)
716 if tgt
.length
> 0 then target_items
= tgt
.items
720 redef fun index
do return curr_pos
722 redef fun is_ok
do return curr_pos
< target
.length
724 redef fun item
do return target_items
[curr_pos
]
726 redef fun next
do curr_pos
+= 1
730 redef class NativeString
733 return to_s_with_length
(cstring_length
)
736 # Returns `self` as a String of `length`.
737 redef fun to_s_with_length
(length
): FlatString
740 var str
= new FlatString.with_infos
(self, length
, 0, length
- 1)
744 # Returns `self` as a new String.
745 redef fun to_s_with_copy
: FlatString
747 var length
= cstring_length
748 var new_self
= new NativeString(length
+ 1)
749 copy_to
(new_self
, length
, 0, 0)
750 var str
= new FlatString.with_infos
(new_self
, length
, 0, length
- 1)
751 new_self
[length
] = '\0'
752 str
.real_items
= new_self
758 redef fun to_base
(base
, signed
)
760 var l
= digit_count
(base
)
761 var s
= new FlatBuffer.from
(" " * l
)
762 fill_buffer
(s
, base
, signed
)
766 # return displayable int in base 10 and signed
768 # assert 1.to_s == "1"
769 # assert (-123).to_s == "-123"
771 # Fast case for common numbers
772 if self == 0 then return "0"
773 if self == 1 then return "1"
775 var nslen
= int_to_s_len
776 var ns
= new NativeString(nslen
+ 1)
778 native_int_to_s
(ns
, nslen
+ 1)
779 return ns
.to_s_with_length
(nslen
)
785 # Fast implementation
789 if l
== 0 then return ""
790 if l
== 1 then if self[0] == null then return "" else return self[0].to_s
792 var na
= new NativeArray[String](l
)
808 var ns
= new NativeString(sl
+ 1)
815 if tmp
isa FlatString then
816 tmp
.items
.copy_to
(ns
, tpl
, tmp
.index_from
, off
)
819 for j
in tmp
.substrings
do
820 var s
= j
.as(FlatString)
822 s
.items
.copy_to
(ns
, slen
, s
.index_from
, off
)
828 return ns
.to_s_with_length
(sl
)
832 redef class NativeArray[E
]
833 redef fun native_to_s
do
834 assert self isa NativeArray[String]
845 var ns
= new NativeString(sl
+ 1)
852 if tmp
isa FlatString then
853 tmp
.items
.copy_to
(ns
, tpl
, tmp
.index_from
, off
)
856 for j
in tmp
.substrings
do
857 var s
= j
.as(FlatString)
859 s
.items
.copy_to
(ns
, slen
, s
.index_from
, off
)
865 return ns
.to_s_with_length
(sl
)
870 redef fun join
(sep
, couple_sep
)
872 if is_empty
then return ""
874 var s
= new Buffer # Result
880 s
.append
("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
888 s
.append
("{k or else "<null>"}{couple_sep}{e or else "<null>"}")