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
49 redef var bytes
= new FlatStringByteView(self) is lazy
53 # Check that the index (+ index_from) is not larger than indexTo
54 # In other terms, if the index is valid
56 assert (index
+ index_from
) <= index_to
57 return items
[index
+ index_from
].to_i
.ascii
60 ################################################
61 # AbstractString specific methods #
62 ################################################
66 var native
= new NativeString(self.length
+ 1)
67 var length
= self.length
68 var items
= self.items
72 native
[pos
] = items
[ipos
]
76 return native
.to_s_with_length
(self.length
)
79 redef fun fast_cstring
do return items
.fast_cstring
(index_from
)
81 redef fun substring
(from
, count
)
87 if count
< 0 then count
= 0
91 var new_from
= index_from
+ from
93 if (new_from
+ count
) > index_to
then
94 var new_len
= index_to
- new_from
+ 1
95 if new_len
<= 0 then return empty
96 return new FlatString.with_infos
(items
, new_len
, new_from
, index_to
)
99 if count
<= 0 then return empty
101 var to
= new_from
+ count
- 1
103 return new FlatString.with_infos
(items
, to
- new_from
+ 1, new_from
, to
)
106 redef fun empty
do return "".as(FlatString)
110 var outstr
= new FlatBuffer.with_capacity
(self.bytelen
+ 1)
116 outstr
.add
(chars
[pos
].to_upper
)
125 var outstr
= new FlatBuffer.with_capacity
(self.bytelen
+ 1)
131 outstr
.add
(chars
[pos
].to_lower
)
140 for i
in chars
do i
.output
143 ##################################################
144 # String Specific Methods #
145 ##################################################
147 # Low-level creation of a new string with given data.
149 # `items` will be used as is, without copy, to retrieve the characters of the string.
150 # Aliasing issues is the responsibility of the caller.
151 private init with_infos
(items
: NativeString, length
: Int, from
: Int, to
: Int)
161 if real_items
!= null then
162 return real_items
.as(not null)
164 var newItems
= new NativeString(length
+ 1)
165 self.items
.copy_to
(newItems
, length
, index_from
, 0)
166 newItems
[length
] = 0u8
167 self.real_items
= newItems
174 if not other
isa FlatString then return super
176 if self.object_id
== other
.object_id
then return true
178 var my_length
= length
180 if other
.length
!= my_length
then return false
182 var my_index
= index_from
183 var its_index
= other
.index_from
185 var last_iteration
= my_index
+ my_length
187 var itsitems
= other
.items
188 var myitems
= self.items
190 while my_index
< last_iteration
do
191 if myitems
[my_index
] != itsitems
[its_index
] then return false
201 if not other
isa FlatString then return super
203 if self.object_id
== other
.object_id
then return false
205 var my_curr_char
: Char
206 var its_curr_char
: Char
208 var my_length
= self.length
209 var its_length
= other
.length
212 if my_length
< its_length
then
219 var its_chars
= other
.chars
223 my_curr_char
= my_chars
[pos
]
224 its_curr_char
= its_chars
[pos
]
226 if my_curr_char
!= its_curr_char
then
227 if my_curr_char
< its_curr_char
then return true
234 return my_length
< its_length
239 var my_length
= self.length
240 var its_length
= s
.length
242 var total_length
= my_length
+ its_length
244 var target_string
= new NativeString(my_length
+ its_length
+ 1)
246 self.items
.copy_to
(target_string
, my_length
, index_from
, 0)
247 if s
isa FlatString then
248 s
.items
.copy_to
(target_string
, its_length
, s
.index_from
, my_length
)
249 else if s
isa FlatBuffer then
250 s
.items
.copy_to
(target_string
, its_length
, 0, my_length
)
252 var curr_pos
= my_length
253 for i
in [0 .. s
.bytelen
[ do
254 target_string
[curr_pos
] = s
.bytes
[i
]
259 target_string
[total_length
] = 0u8
261 return target_string
.to_s_with_length
(total_length
)
268 var my_length
= self.length
270 var final_length
= my_length
* i
272 var my_items
= self.items
274 var target_string
= new NativeString(final_length
+ 1)
276 target_string
[final_length
] = 0u8
280 for iteration
in [1 .. i
] do
281 my_items
.copy_to
(target_string
, my_length
, 0, current_last
)
282 current_last
+= my_length
285 return target_string
.to_s_with_length
(final_length
)
290 if hash_cache
== null then
291 # djb2 hash algorithm
297 while i
<= index_to
do
298 h
= h
.lshift
(5) + h
+ myitems
[i
].to_i
305 return hash_cache
.as(not null)
308 redef fun substrings
do return new FlatSubstringsIter(self)
311 private class FlatStringCharReverseIterator
312 super IndexedIterator[Char]
314 var target
: FlatString
318 init with_pos
(tgt
: FlatString, pos
: Int)
323 redef fun is_ok
do return curr_pos
>= 0
325 redef fun item
do return target
[curr_pos
]
327 redef fun next
do curr_pos
-= 1
329 redef fun index
do return curr_pos
333 private class FlatStringCharIterator
334 super IndexedIterator[Char]
336 var target
: FlatString
342 init with_pos
(tgt
: FlatString, pos
: Int)
344 init(tgt
, tgt
.length
- 1, pos
)
347 redef fun is_ok
do return curr_pos
<= max
349 redef fun item
do return target
[curr_pos
]
351 redef fun next
do curr_pos
+= 1
353 redef fun index
do return curr_pos
357 private class FlatStringCharView
360 redef type SELFTYPE: FlatString
362 redef fun [](index
) do return target
[index
]
364 redef fun iterator_from
(start
) do return new FlatStringCharIterator.with_pos
(target
, start
)
366 redef fun reverse_iterator_from
(start
) do return new FlatStringCharReverseIterator.with_pos
(target
, start
)
370 private class FlatStringByteReverseIterator
371 super IndexedIterator[Byte]
373 var target
: FlatString
375 var target_items
: NativeString
379 init with_pos
(tgt
: FlatString, pos
: Int)
381 init(tgt
, tgt
.items
, pos
+ tgt
.index_from
)
384 redef fun is_ok
do return curr_pos
>= target
.index_from
386 redef fun item
do return target_items
[curr_pos
]
388 redef fun next
do curr_pos
-= 1
390 redef fun index
do return curr_pos
- target
.index_from
394 private class FlatStringByteIterator
395 super IndexedIterator[Byte]
397 var target
: FlatString
399 var target_items
: NativeString
403 init with_pos
(tgt
: FlatString, pos
: Int)
405 init(tgt
, tgt
.items
, pos
+ tgt
.index_from
)
408 redef fun is_ok
do return curr_pos
<= target
.index_to
410 redef fun item
do return target_items
[curr_pos
]
412 redef fun next
do curr_pos
+= 1
414 redef fun index
do return curr_pos
- target
.index_from
418 private class FlatStringByteView
421 redef type SELFTYPE: FlatString
425 # Check that the index (+ index_from) is not larger than indexTo
426 # In other terms, if the index is valid
428 var target
= self.target
429 assert (index
+ target
.index_from
) <= target
.index_to
430 return target
.items
[index
+ target
.index_from
]
433 redef fun iterator_from
(start
) do return new FlatStringByteIterator.with_pos
(target
, start
)
435 redef fun reverse_iterator_from
(start
) do return new FlatStringByteReverseIterator.with_pos
(target
, start
)
440 redef new do return new FlatBuffer
442 redef new with_cap
(i
) do return new FlatBuffer.with_capacity
(i
)
445 # Mutable strings of characters.
450 redef var chars
: Sequence[Char] = new FlatBufferCharView(self) is lazy
452 redef var bytes
: Sequence[Byte] = new FlatBufferByteView(self) is lazy
454 private var capacity
: Int = 0
456 redef fun fast_cstring
do return items
.fast_cstring
(0)
458 redef fun substrings
do return new FlatSubstringsIter(self)
460 # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
462 # This happens when an operation modifies the current `Buffer` and
463 # the Copy-On-Write flag `written` is set at true.
465 var nns
= new NativeString(capacity
)
466 items
.copy_to
(nns
, length
, 0, 0)
474 assert index
< length
475 return items
[index
].to_i
.ascii
478 redef fun []=(index
, item
)
481 if index
== length
then
485 if written
then reset
486 assert index
>= 0 and index
< length
487 items
[index
] = item
.ascii
.to_b
493 if capacity
<= length
then enlarge
(length
+ 5)
494 items
[length
] = c
.ascii
.to_b
498 private fun add_byte
(b
: Byte) do
500 if capacity
<= length
then enlarge
(length
+ 5)
507 if written
then reset
511 redef fun empty
do return new Buffer
513 redef fun enlarge
(cap
)
516 if cap
<= c
then return
517 while c
<= cap
do c
= c
* 2 + 2
518 # The COW flag can be set at false here, since
519 # it does a copy of the current `Buffer`
521 var a
= new NativeString(c
+1)
522 if length
> 0 then items
.copy_to
(a
, length
, 0, 0)
530 if length
== 0 then items
= new NativeString(1)
531 return new FlatString.with_infos
(items
, length
, 0, length
- 1)
537 var new_native
= new NativeString(length
+ 1)
538 new_native
[length
] = 0u8
539 if length
> 0 then items
.copy_to
(new_native
, length
, 0, 0)
540 real_items
= new_native
543 return real_items
.as(not null)
546 # Create a new empty string.
549 # Low-level creation a new buffer with given data.
551 # `items` will be used as is, without copy, to store the characters of the buffer.
552 # Aliasing issues is the responsibility of the caller.
554 # If `items` is shared, `written` should be set to true after the creation
555 # so that a modification will do a copy-on-write.
556 private init with_infos
(items
: NativeString, capacity
, length
: Int)
560 self.capacity
= capacity
563 # Create a new string copied from `s`.
566 capacity
= s
.length
+ 1
568 items
= new NativeString(capacity
)
569 if s
isa FlatString then
570 s
.items
.copy_to
(items
, length
, s
.index_from
, 0)
571 else if s
isa FlatBuffer then
572 s
.items
.copy_to
(items
, length
, 0, 0)
582 # Create a new empty string with a given capacity.
583 init with_capacity
(cap
: Int)
586 items
= new NativeString(cap
+1)
593 if s
.is_empty
then return
596 if capacity
< length
+ sl
then enlarge
(length
+ sl
)
597 if s
isa FlatString then
598 s
.items
.copy_to
(items
, sl
, s
.index_from
, length
)
599 else if s
isa FlatBuffer then
600 s
.items
.copy_to
(items
, sl
, 0, length
)
602 var curr_pos
= self.length
611 # Copies the content of self in `dest`
612 fun copy
(start
: Int, len
: Int, dest
: Buffer, new_start
: Int)
614 var self_chars
= self.chars
615 var dest_chars
= dest
.chars
616 for i
in [0..len-1
] do
617 dest_chars
[new_start
+i
] = self_chars
[start
+i
]
621 redef fun substring
(from
, count
)
625 if from
< 0 then from
= 0
626 if count
> length
then count
= length
628 var len
= count
- from
629 var r_items
= new NativeString(len
)
630 items
.copy_to
(r_items
, len
, from
, 0)
631 var r
= new FlatBuffer.with_infos
(r_items
, len
, len
)
641 var ns
= new NativeString(capacity
)
653 redef fun times
(repeats
)
655 var x
= new FlatString.with_infos
(items
, length
, 0, length
- 1)
656 for i
in [1..repeats
[ do
663 if written
then reset
666 self[id
] = self[id
].to_upper
673 if written
then reset
676 self[id
] = self[id
].to_lower
682 private class FlatBufferByteReverseIterator
683 super IndexedIterator[Byte]
685 var target
: FlatBuffer
687 var target_items
: NativeString
691 init with_pos
(tgt
: FlatBuffer, pos
: Int)
693 init(tgt
, tgt
.items
, pos
)
696 redef fun index
do return curr_pos
698 redef fun is_ok
do return curr_pos
>= 0
700 redef fun item
do return target_items
[curr_pos
]
702 redef fun next
do curr_pos
-= 1
706 private class FlatBufferByteView
709 redef type SELFTYPE: FlatBuffer
711 redef fun [](index
) do return target
.items
[index
]
713 redef fun []=(index
, item
)
715 assert index
>= 0 and index
<= target
.bytelen
716 if index
== target
.bytelen
then
720 target
.items
[index
] = item
728 fun enlarge
(cap
: Int)
735 var s_length
= s
.length
736 if target
.capacity
< (target
.length
+ s_length
) then enlarge
(s_length
+ target
.length
)
737 var pos
= target
.length
738 var its
= target
.items
743 target
.length
+= s
.length
746 redef fun iterator_from
(pos
) do return new FlatBufferByteIterator.with_pos
(target
, pos
)
748 redef fun reverse_iterator_from
(pos
) do return new FlatBufferByteReverseIterator.with_pos
(target
, pos
)
752 private class FlatBufferByteIterator
753 super IndexedIterator[Byte]
755 var target
: FlatBuffer
757 var target_items
: NativeString
761 init with_pos
(tgt
: FlatBuffer, pos
: Int)
763 init(tgt
, tgt
.items
, pos
)
766 redef fun index
do return curr_pos
768 redef fun is_ok
do return curr_pos
< target
.length
770 redef fun item
do return target_items
[curr_pos
]
772 redef fun next
do curr_pos
+= 1
776 private class FlatBufferCharReverseIterator
777 super IndexedIterator[Char]
779 var target
: FlatBuffer
783 init with_pos
(tgt
: FlatBuffer, pos
: Int)
788 redef fun index
do return curr_pos
790 redef fun is_ok
do return curr_pos
>= 0
792 redef fun item
do return target
[curr_pos
]
794 redef fun next
do curr_pos
-= 1
798 private class FlatBufferCharView
801 redef type SELFTYPE: FlatBuffer
803 redef fun [](index
) do return target
[index
]
805 redef fun []=(index
, item
)
807 assert index
>= 0 and index
<= length
808 if index
== length
then
825 fun enlarge
(cap
: Int)
832 var s_length
= s
.length
833 if target
.capacity
< s
.length
then enlarge
(s_length
+ target
.length
)
834 for i
in s
do target
.add i
837 redef fun iterator_from
(pos
) do return new FlatBufferCharIterator.with_pos
(target
, pos
)
839 redef fun reverse_iterator_from
(pos
) do return new FlatBufferCharReverseIterator.with_pos
(target
, pos
)
843 private class FlatBufferCharIterator
844 super IndexedIterator[Char]
846 var target
: FlatBuffer
852 init with_pos
(tgt
: FlatBuffer, pos
: Int)
854 init(tgt
, tgt
.length
- 1, pos
)
857 redef fun index
do return curr_pos
859 redef fun is_ok
do return curr_pos
<= max
861 redef fun item
do return target
[curr_pos
]
863 redef fun next
do curr_pos
+= 1
867 redef class NativeString
870 return to_s_with_length
(cstring_length
)
873 # Returns `self` as a String of `length`.
874 redef fun to_s_with_length
(length
): FlatString
877 var str
= new FlatString.with_infos
(self, length
, 0, length
- 1)
881 # Returns `self` as a new String.
882 redef fun to_s_with_copy
: FlatString
884 var length
= cstring_length
885 var new_self
= new NativeString(length
+ 1)
886 copy_to
(new_self
, length
, 0, 0)
887 var str
= new FlatString.with_infos
(new_self
, length
, 0, length
- 1)
888 new_self
[length
] = 0u8
889 str
.real_items
= new_self
895 redef fun to_base
(base
, signed
)
897 var l
= digit_count
(base
)
898 var s
= new FlatBuffer.from
(" " * l
)
899 fill_buffer
(s
, base
, signed
)
903 # return displayable int in base 10 and signed
905 # assert 1.to_s == "1"
906 # assert (-123).to_s == "-123"
908 # Fast case for common numbers
909 if self == 0 then return "0"
910 if self == 1 then return "1"
912 var nslen
= int_to_s_len
913 var ns
= new NativeString(nslen
+ 1)
915 native_int_to_s
(ns
, nslen
+ 1)
916 return ns
.to_s_with_length
(nslen
)
922 # Fast implementation
926 if l
== 0 then return ""
927 if l
== 1 then if self[0] == null then return "" else return self[0].to_s
929 var na
= new NativeArray[String](l
)
945 var ns
= new NativeString(sl
+ 1)
952 if tmp
isa FlatString then
953 tmp
.items
.copy_to
(ns
, tpl
, tmp
.index_from
, off
)
956 for j
in tmp
.substrings
do
957 var s
= j
.as(FlatString)
959 s
.items
.copy_to
(ns
, slen
, s
.index_from
, off
)
965 return ns
.to_s_with_length
(sl
)
969 redef class NativeArray[E
]
970 redef fun native_to_s
do
971 assert self isa NativeArray[String]
982 var ns
= new NativeString(sl
+ 1)
989 if tmp
isa FlatString then
990 tmp
.items
.copy_to
(ns
, tpl
, tmp
.index_from
, off
)
993 for j
in tmp
.substrings
do
994 var s
= j
.as(FlatString)
996 s
.items
.copy_to
(ns
, slen
, s
.index_from
, off
)
1002 return ns
.to_s_with_length
(sl
)
1006 redef class Map[K
,V
]
1007 redef fun join
(sep
, couple_sep
)
1009 if is_empty
then return ""
1011 var s
= new Buffer # Result
1017 s
.append
("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1019 # Concat other items
1025 s
.append
("{k or else "<null>"}{couple_sep}{e or else "<null>"}")