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
22 private class FlatSubstringsIter
23 super Iterator[FlatText]
25 var tgt
: nullable FlatText
29 return tgt
.as(not null)
32 redef fun is_ok
do return tgt
!= null
34 redef fun next
do tgt
= null
39 private fun first_byte
: Int do return 0
41 private fun last_byte
: Int do return _bytelen
- 1
43 # Cache of the latest position (char) explored in the string
44 private var position
: Int = 0
46 # Cached position (bytes) in the NativeString underlying the String
47 private var bytepos
: Int = 0
49 # Index of the character `index` in `_items`
50 private fun char_to_byte_index
(index
: Int): Int do
56 # Find best insertion point
57 var delta_begin
= index
58 var delta_end
= (ln
- 1) - index
59 var delta_cache
= (pos
- index
).abs
63 if delta_cache
< min
then min
= delta_cache
64 if delta_end
< min
then min
= delta_end
69 if min
== delta_begin
then
72 else if min
== delta_cache
then
76 ns_i
= its
.find_beginning_of_char_at
(last_byte
)
80 ns_i
= its
.char_to_byte_index_cached
(index
, my_i
, ns_i
)
88 private fun byte_to_char_index
(index
: Int): Int do
94 # Find best insertion point
95 var delta_begin
= index
96 var delta_end
= (ln
- 1) - index
97 var delta_cache
= (pos
- index
).abs
101 if delta_cache
< min
then min
= delta_cache
102 if delta_end
< min
then min
= delta_end
107 if min
== delta_begin
then
110 else if min
== delta_cache
then
114 ns_i
= its
.find_beginning_of_char_at
(last_byte
)
118 my_i
= its
.byte_to_char_index_cached
(index
, my_i
, ns_i
)
126 redef fun [](index
) do return _items
.char_at
(char_to_byte_index
(index
))
129 # Immutable strings of characters.
134 # Index at which `self` begins in `_items`, inclusively
135 redef var first_byte
is noinit
137 # Index at which `self` ends in `_items`, inclusively
138 redef var last_byte
is noinit
140 redef var chars
= new FlatStringCharView(self) is lazy
142 redef var bytes
= new FlatStringByteView(self) is lazy
144 redef var length
is lazy
do
145 if _bytelen
== 0 then return 0
151 st
+= its
.length_of_char_at
(st
)
159 var b
= new FlatBuffer.with_capacity
(_bytelen
+ 1)
160 for i
in [length
- 1 .. 0].step
(-1) do
163 var s
= b
.to_s
.as(FlatString)
164 s
.length
= self.length
168 redef fun fast_cstring
do return _items
.fast_cstring
(_first_byte
)
170 redef fun substring
(from
, count
)
176 if count
< 0 then count
= 0
180 if (count
+ from
) > length
then count
= length
- from
181 if count
<= 0 then return ""
182 var end_index
= from
+ count
- 1
184 var bytefrom
= char_to_byte_index
(from
)
185 var byteto
= char_to_byte_index
(end_index
)
187 byteto
+= its
.length_of_char_at
(byteto
) - 1
189 var s
= new FlatString.full
(its
, byteto
- bytefrom
+ 1, bytefrom
, byteto
, count
)
193 redef fun empty
do return "".as(FlatString)
197 var outstr
= new FlatBuffer.with_capacity
(self._bytelen
+ 1)
203 outstr
.add
(chars
[pos
].to_upper
)
212 var outstr
= new FlatBuffer.with_capacity
(self._bytelen
+ 1)
218 outstr
.add
(chars
[pos
].to_lower
)
227 for i
in chars
do i
.output
230 ##################################################
231 # String Specific Methods #
232 ##################################################
234 # Low-level creation of a new string with minimal data.
236 # `_items` will be used as is, without copy, to retrieve the characters of the string.
237 # Aliasing issues is the responsibility of the caller.
238 private init with_infos
(items
: NativeString, bytelen
, from
, to
: Int)
241 self._bytelen
= bytelen
247 # Low-level creation of a new string with all the data.
249 # `_items` will be used as is, without copy, to retrieve the characters of the string.
250 # Aliasing issues is the responsibility of the caller.
251 private init full
(items
: NativeString, bytelen
, from
, to
, length
: Int)
255 self._bytelen
= bytelen
261 redef fun to_cstring
do
262 if real_items
!= null then return real_items
.as(not null)
264 var new_items
= new NativeString(blen
+ 1)
265 _items
.copy_to
(new_items
, blen
, _first_byte
, 0)
266 new_items
[blen
] = 0u8
267 real_items
= new_items
273 if not other
isa FlatString then return super
275 if self.object_id
== other
.object_id
then return true
277 var my_length
= _bytelen
279 if other
._bytelen
!= my_length
then return false
281 var my_index
= _first_byte
282 var its_index
= other
._first_byte
284 var last_iteration
= my_index
+ my_length
286 var its_items
= other
._items
287 var my_items
= self._items
289 while my_index
< last_iteration
do
290 if my_items
[my_index
] != its_items
[its_index
] then return false
300 if not other
isa FlatString then return super
302 if self.object_id
== other
.object_id
then return false
304 var my_length
= self._bytelen
305 var its_length
= other
._bytelen
307 var max
= if my_length
< its_length
then my_length
else its_length
309 var myits
= self.bytes
310 var itsits
= other
.bytes
312 for i
in [0 .. max
[ do
313 var my_curr_char
= myits
[i
]
314 var its_curr_char
= itsits
[i
]
316 if my_curr_char
!= its_curr_char
then
317 if my_curr_char
< its_curr_char
then return true
322 return my_length
< its_length
329 var nlen
= mlen
+ slen
331 var mifrom
= _first_byte
332 if s
isa FlatText then
334 var sifrom
= s
.first_byte
335 var ns
= new NativeString(nlen
+ 1)
336 mits
.copy_to
(ns
, mlen
, mifrom
, 0)
337 sits
.copy_to
(ns
, slen
, sifrom
, mlen
)
338 return new FlatString.full
(ns
, nlen
, 0, nlen
- 1, length
+ o
.length
)
345 var mybtlen
= _bytelen
346 var new_bytelen
= mybtlen
* i
348 var newlen
= mylen
* i
351 var ns
= new NativeString(new_bytelen
+ 1)
352 ns
[new_bytelen
] = 0u8
355 its
.copy_to
(ns
, mybtlen
, fb
, offset
)
359 return new FlatString.full
(ns
, new_bytelen
, 0, new_bytelen
- 1, newlen
)
365 if hash_cache
== null then
366 # djb2 hash algorithm
370 var my_items
= _items
374 h
= (h
<< 5) + h
+ my_items
[i
].to_i
381 return hash_cache
.as(not null)
384 redef fun substrings
do return new FlatSubstringsIter(self)
387 private class FlatStringCharReverseIterator
388 super IndexedIterator[Char]
390 var target
: FlatString
394 init with_pos
(tgt
: FlatString, pos
: Int)
399 redef fun is_ok
do return curr_pos
>= 0
401 redef fun item
do return target
[curr_pos
]
403 redef fun next
do curr_pos
-= 1
405 redef fun index
do return curr_pos
409 private class FlatStringCharIterator
410 super IndexedIterator[Char]
412 var target
: FlatString
418 init with_pos
(tgt
: FlatString, pos
: Int)
420 init(tgt
, tgt
.length
- 1, pos
)
423 redef fun is_ok
do return curr_pos
<= max
425 redef fun item
do return target
[curr_pos
]
427 redef fun next
do curr_pos
+= 1
429 redef fun index
do return curr_pos
433 private class FlatStringCharView
436 redef type SELFTYPE: FlatString
438 redef fun [](index
) do return target
[index
]
440 redef fun iterator_from
(start
) do return new FlatStringCharIterator.with_pos
(target
, start
)
442 redef fun reverse_iterator_from
(start
) do return new FlatStringCharReverseIterator.with_pos
(target
, start
)
446 private class FlatStringByteReverseIterator
447 super IndexedIterator[Byte]
449 var target
: FlatString
451 var target_items
: NativeString
455 init with_pos
(tgt
: FlatString, pos
: Int)
457 init(tgt
, tgt
._items
, pos
+ tgt
._first_byte
)
460 redef fun is_ok
do return curr_pos
>= target
._first_byte
462 redef fun item
do return target_items
[curr_pos
]
464 redef fun next
do curr_pos
-= 1
466 redef fun index
do return curr_pos
- target
._first_byte
470 private class FlatStringByteIterator
471 super IndexedIterator[Byte]
473 var target
: FlatString
475 var target_items
: NativeString
479 init with_pos
(tgt
: FlatString, pos
: Int)
481 init(tgt
, tgt
._items
, pos
+ tgt
._first_byte
)
484 redef fun is_ok
do return curr_pos
<= target
._last_byte
486 redef fun item
do return target_items
[curr_pos
]
488 redef fun next
do curr_pos
+= 1
490 redef fun index
do return curr_pos
- target
._first_byte
494 private class FlatStringByteView
497 redef type SELFTYPE: FlatString
501 # Check that the index (+ _first_byte) is not larger than _last_byte
502 # In other terms, if the index is valid
504 var target
= self.target
505 var ind
= index
+ target
._first_byte
506 assert ind
<= target
._last_byte
507 return target
._items
[ind
]
510 redef fun iterator_from
(start
) do return new FlatStringByteIterator.with_pos
(target
, start
)
512 redef fun reverse_iterator_from
(start
) do return new FlatStringByteReverseIterator.with_pos
(target
, start
)
517 redef new do return new FlatBuffer
519 redef new with_cap
(i
) do return new FlatBuffer.with_capacity
(i
)
522 # Mutable strings of characters.
527 redef var chars
: Sequence[Char] = new FlatBufferCharView(self) is lazy
529 redef var bytes
= new FlatBufferByteView(self) is lazy
533 private var char_cache
: Int = -1
535 private var byte_cache
: Int = -1
537 private var capacity
= 0
539 redef fun fast_cstring
do return _items
.fast_cstring
(0)
541 redef fun substrings
do return new FlatSubstringsIter(self)
543 # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
545 # This happens when an operation modifies the current `Buffer` and
546 # the Copy-On-Write flag `written` is set at true.
548 var nns
= new NativeString(capacity
)
549 if _bytelen
!= 0 then _items
.copy_to
(nns
, _bytelen
, 0, 0)
554 # Shifts the content of the buffer by `len` bytes to the right, starting at byte `from`
556 # Internal only, does not modify _bytelen or length, this is the caller's responsability
557 private fun rshift_bytes
(from
: Int, len
: Int) do
561 if bt
+ len
> capacity
then
562 capacity
= capacity
* 2 + 2
563 nit
= new NativeString(capacity
)
564 oit
.copy_to
(nit
, 0, 0, from
)
566 oit
.copy_to
(nit
, bt
- from
, from
, from
+ len
)
569 # Shifts the content of the buffer by `len` bytes to the left, starting at `from`
571 # Internal only, does not modify _bytelen or length, this is the caller's responsability
572 private fun lshift_bytes
(from
: Int, len
: Int) do
574 it
.copy_to
(it
, _bytelen
- from
, from
, from
- len
)
577 redef fun []=(index
, item
)
579 assert index
>= 0 and index
<= length
580 if written
then reset
582 if index
== length
then
587 var ip
= it
.char_to_byte_index
(index
)
588 var c
= it
.char_at
(ip
)
589 var clen
= c
.u8char_len
590 var itemlen
= item
.u8char_len
591 var size_diff
= itemlen
- clen
592 if size_diff
> 0 then
593 rshift_bytes
(ip
+ clen
, size_diff
)
594 else if size_diff
< 0 then
595 lshift_bytes
(ip
+ clen
, -size_diff
)
597 _bytelen
+= size_diff
599 it
.set_char_at
(ip
, item
)
604 if written
then reset
606 var clen
= c
.u8char_len
609 _items
.set_char_at
(bt
, c
)
616 if written
then reset
621 redef fun empty
do return new Buffer
623 redef fun enlarge
(cap
)
626 if cap
<= c
then return
627 while c
<= cap
do c
= c
* 2 + 2
628 # The COW flag can be set at false here, since
629 # it does a copy of the current `Buffer`
632 var a
= new NativeString(c
+1)
635 if bln
> 0 then it
.copy_to
(a
, bln
, 0, 0)
645 if bln
== 0 then _items
= new NativeString(1)
646 return new FlatString.full
(_items
, bln
, 0, bln
- 1, length
)
653 var new_native
= new NativeString(bln
+ 1)
654 new_native
[bln
] = 0u8
655 if length
> 0 then _items
.copy_to
(new_native
, bln
, 0, 0)
656 real_items
= new_native
659 return real_items
.as(not null)
662 # Create a new empty string.
665 # Low-level creation a new buffer with given data.
667 # `_items` will be used as is, without copy, to store the characters of the buffer.
668 # Aliasing issues is the responsibility of the caller.
670 # If `_items` is shared, `written` should be set to true after the creation
671 # so that a modification will do a copy-on-write.
672 private init with_infos
(items
: NativeString, capacity
, bytelen
, length
: Int)
675 self.capacity
= capacity
676 self._bytelen
= bytelen
680 # Create a new string copied from `s`.
683 _items
= new NativeString(s
.bytelen
)
684 if s
isa FlatText then
687 for i
in substrings
do i
.as(FlatString)._items
.copy_to
(_items
, i
._bytelen
, 0, 0)
695 # Create a new empty string with a given capacity.
696 init with_capacity
(cap
: Int)
699 _items
= new NativeString(cap
+ 1)
706 if s
.is_empty
then return
709 var nln
= _bytelen
+ sl
711 if s
isa FlatText then
712 s
._items
.copy_to
(_items
, sl
, s
.first_byte
, _bytelen
)
714 for i
in s
.substrings
do append i
721 # Copies the content of self in `dest`
722 fun copy
(start
: Int, len
: Int, dest
: Buffer, new_start
: Int)
724 var self_chars
= self.chars
725 var dest_chars
= dest
.chars
726 for i
in [0..len-1
] do
727 dest_chars
[new_start
+i
] = self_chars
[start
+i
]
731 redef fun substring
(from
, count
)
734 if from
< 0 then from
= 0
735 if (from
+ count
) > length
then count
= length
- from
738 var bytefrom
= its
.char_to_byte_index
(from
)
739 var byteto
= its
.char_to_byte_index
(count
+ from
- 1)
740 byteto
+= its
.char_at
(byteto
).u8char_len
- 1
741 var byte_length
= byteto
- bytefrom
+ 1
742 var r_items
= new NativeString(byte_length
)
743 its
.copy_to
(r_items
, byte_length
, bytefrom
, 0)
744 return new FlatBuffer.with_infos
(r_items
, byte_length
, byte_length
, count
)
753 var ns
= new FlatBuffer.with_capacity
(capacity
)
754 for i
in chars
.reverse_iterator
do ns
.add i
758 redef fun times
(repeats
)
761 var x
= new FlatString.full
(_items
, bln
, 0, bln
- 1, length
)
762 for i
in [1 .. repeats
[ do
769 if written
then reset
770 for i
in [0 .. length
[ do self[i
] = self[i
].to_upper
775 if written
then reset
776 for i
in [0 .. length
[ do self[i
] = self[i
].to_lower
780 private class FlatBufferByteReverseIterator
781 super IndexedIterator[Byte]
783 var target
: FlatBuffer
785 var target_items
: NativeString
789 init with_pos
(tgt
: FlatBuffer, pos
: Int)
791 init(tgt
, tgt
._items
, pos
)
794 redef fun index
do return curr_pos
796 redef fun is_ok
do return curr_pos
>= 0
798 redef fun item
do return target_items
[curr_pos
]
800 redef fun next
do curr_pos
-= 1
804 private class FlatBufferByteView
807 redef type SELFTYPE: FlatBuffer
809 redef fun [](index
) do return target
._items
[index
]
811 redef fun iterator_from
(pos
) do return new FlatBufferByteIterator.with_pos
(target
, pos
)
813 redef fun reverse_iterator_from
(pos
) do return new FlatBufferByteReverseIterator.with_pos
(target
, pos
)
817 private class FlatBufferByteIterator
818 super IndexedIterator[Byte]
820 var target
: FlatBuffer
822 var target_items
: NativeString
826 init with_pos
(tgt
: FlatBuffer, pos
: Int)
828 init(tgt
, tgt
._items
, pos
)
831 redef fun index
do return curr_pos
833 redef fun is_ok
do return curr_pos
< target
._bytelen
835 redef fun item
do return target_items
[curr_pos
]
837 redef fun next
do curr_pos
+= 1
841 private class FlatBufferCharReverseIterator
842 super IndexedIterator[Char]
844 var target
: FlatBuffer
848 init with_pos
(tgt
: FlatBuffer, pos
: Int)
853 redef fun index
do return curr_pos
855 redef fun is_ok
do return curr_pos
>= 0
857 redef fun item
do return target
[curr_pos
]
859 redef fun next
do curr_pos
-= 1
863 private class FlatBufferCharView
866 redef type SELFTYPE: FlatBuffer
868 redef fun [](index
) do return target
[index
]
870 redef fun []=(index
, item
)
872 assert index
>= 0 and index
<= length
873 if index
== length
then
890 fun enlarge
(cap
: Int)
897 var s_length
= s
.length
898 if target
.capacity
< s
.length
then enlarge
(s_length
+ target
.length
)
899 for i
in s
do target
.add i
902 redef fun iterator_from
(pos
) do return new FlatBufferCharIterator.with_pos
(target
, pos
)
904 redef fun reverse_iterator_from
(pos
) do return new FlatBufferCharReverseIterator.with_pos
(target
, pos
)
908 private class FlatBufferCharIterator
909 super IndexedIterator[Char]
911 var target
: FlatBuffer
917 init with_pos
(tgt
: FlatBuffer, pos
: Int)
919 init(tgt
, tgt
.length
- 1, pos
)
922 redef fun index
do return curr_pos
924 redef fun is_ok
do return curr_pos
<= max
926 redef fun item
do return target
[curr_pos
]
928 redef fun next
do curr_pos
+= 1
932 redef class NativeString
935 return to_s_with_length
(cstring_length
)
938 # Returns `self` as a String of `length`.
939 redef fun to_s_with_length
(length
): FlatString
942 var str
= new FlatString.with_infos
(self, length
, 0, length
- 1)
946 redef fun to_s_full
(bytelen
, unilen
) do
947 return new FlatString.full
(self, bytelen
, 0, bytelen
- 1, unilen
)
950 # Returns `self` as a new String.
951 redef fun to_s_with_copy
: FlatString
953 var length
= cstring_length
954 var new_self
= new NativeString(length
+ 1)
955 copy_to
(new_self
, length
, 0, 0)
956 var str
= new FlatString.with_infos
(new_self
, length
, 0, length
- 1)
957 new_self
[length
] = 0u8
958 str
.real_items
= new_self
962 # Sets the next bytes at position `pos` to the value of `c`, encoded in UTF-8
964 # Very unsafe, make sure to have room for this char prior to calling this function.
965 private fun set_char_at
(pos
: Int, c
: Char) do
966 var ln
= c
.u8char_len
967 native_set_char
(pos
, c
, ln
)
970 private fun native_set_char
(pos
: Int, c
: Char, ln
: Int) `{
971 char* dst = self + pos;
977 dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
978 dst[1] = 0x80 | (c & 0x3F);
981 dst[0] = 0xE0 | ((c & 0xF000) >> 12);
982 dst[1] = 0x80 | ((c & 0xFC0) >> 6);
983 dst[2] = 0x80 | (c & 0x3F);
986 dst[0] = 0xF0 | ((c & 0x1C0000) >> 18);
987 dst[1] = 0x80 | ((c & 0x3F000) >> 12);
988 dst[2] = 0x80 | ((c & 0xFC0) >> 6);
989 dst[3] = 0x80 | (c & 0x3F);
996 redef fun to_base
(base
, signed
)
998 var l
= digit_count
(base
)
999 var s
= new FlatBuffer.from
(" " * l
)
1000 fill_buffer
(s
, base
, signed
)
1004 # return displayable int in base 10 and signed
1006 # assert 1.to_s == "1"
1007 # assert (-123).to_s == "-123"
1009 # Fast case for common numbers
1010 if self == 0 then return "0"
1011 if self == 1 then return "1"
1013 var nslen
= int_to_s_len
1014 var ns
= new NativeString(nslen
+ 1)
1016 native_int_to_s
(ns
, nslen
+ 1)
1017 return new FlatString.full
(ns
, nslen
, 0, nslen
- 1, nslen
)
1021 redef class Array[E
]
1023 # Fast implementation
1024 redef fun plain_to_s
1027 if l
== 0 then return ""
1028 if l
== 1 then if self[0] == null then return "" else return self[0].to_s
1030 var na
= new NativeArray[String](l
)
1036 if itsi
== null then
1046 var ns
= new NativeString(sl
+ 1)
1052 if tmp
isa FlatString then
1053 var tpl
= tmp
._bytelen
1054 tmp
._items
.copy_to
(ns
, tpl
, tmp
._first_byte
, off
)
1057 for j
in tmp
.substrings
do
1058 var s
= j
.as(FlatString)
1059 var slen
= s
._bytelen
1060 s
._items
.copy_to
(ns
, slen
, s
._first_byte
, off
)
1066 return ns
.to_s_with_length
(sl
)
1070 redef class NativeArray[E
]
1071 redef fun native_to_s
do
1072 assert self isa NativeArray[String]
1083 var ns
= new NativeString(sl
+ 1)
1089 if tmp
isa FlatString then
1090 var tpl
= tmp
._bytelen
1091 tmp
._items
.copy_to
(ns
, tpl
, tmp
._first_byte
, off
)
1094 for j
in tmp
.substrings
do
1095 var s
= j
.as(FlatString)
1096 var slen
= s
._bytelen
1097 s
._items
.copy_to
(ns
, slen
, s
._first_byte
, off
)
1103 return ns
.to_s_with_length
(sl
)
1107 redef class Map[K
,V
]
1108 redef fun join
(sep
, couple_sep
)
1110 if is_empty
then return ""
1112 var s
= new Buffer # Result
1118 s
.append
("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1120 # Concat other _items
1126 s
.append
("{k or else "<null>"}{couple_sep}{e or else "<null>"}")