lib/core/flat: `bytepos` and `position` cache are now private
[nit.git] / lib / core / text / flat.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
9 # another product.
10
11 # All the array-based text representations
12 module flat
13
14 intrude import abstract_text
15 intrude import native
16
17 `{
18 #include <stdio.h>
19 #include <string.h>
20 `}
21
22 private class FlatSubstringsIter
23 super Iterator[FlatText]
24
25 var tgt: nullable FlatText
26
27 redef fun item do
28 assert is_ok
29 return tgt.as(not null)
30 end
31
32 redef fun is_ok do return tgt != null
33
34 redef fun next do tgt = null
35 end
36
37 redef class FlatText
38
39 private fun first_byte: Int do return 0
40
41 private fun last_byte: Int do return _bytelen - 1
42
43 # Cache of the latest position (char) explored in the string
44 private var position: Int = 0
45
46 # Cached position (bytes) in the NativeString underlying the String
47 private var bytepos: Int = 0
48
49 # Index of the character `index` in `_items`
50 private fun char_to_byte_index(index: Int): Int do
51 var ln = length
52 assert index >= 0
53 assert index < ln
54
55 var pos = _position
56 # Find best insertion point
57 var delta_begin = index
58 var delta_end = (ln - 1) - index
59 var delta_cache = (pos - index).abs
60 var min = delta_begin
61 var its = _items
62
63 if delta_cache < min then min = delta_cache
64 if delta_end < min then min = delta_end
65
66 var ns_i: Int
67 var my_i: Int
68
69 if min == delta_begin then
70 ns_i = first_byte
71 my_i = 0
72 else if min == delta_cache then
73 ns_i = _bytepos
74 my_i = pos
75 else
76 ns_i = its.find_beginning_of_char_at(last_byte)
77 my_i = length - 1
78 end
79
80 ns_i = its.char_to_byte_index_cached(index, my_i, ns_i)
81
82 _position = index
83 _bytepos = ns_i
84
85 return ns_i
86 end
87
88 private fun byte_to_char_index(index: Int): Int do
89 var ln = _bytelen
90 assert index >= 0
91 assert index < ln
92
93 var pos = _bytepos
94 # Find best insertion point
95 var delta_begin = index
96 var delta_end = (ln - 1) - index
97 var delta_cache = (pos - index).abs
98 var min = delta_begin
99 var its = _items
100
101 if delta_cache < min then min = delta_cache
102 if delta_end < min then min = delta_end
103
104 var ns_i: Int
105 var my_i: Int
106
107 if min == delta_begin then
108 ns_i = first_byte
109 my_i = 0
110 else if min == delta_cache then
111 ns_i = pos
112 my_i = _position
113 else
114 ns_i = its.find_beginning_of_char_at(last_byte)
115 my_i = length - 1
116 end
117
118 my_i = its.byte_to_char_index_cached(index, my_i, ns_i)
119
120 _position = my_i
121 _bytepos = index
122
123 return my_i
124 end
125
126 redef fun [](index) do return _items.char_at(char_to_byte_index(index))
127 end
128
129 # Immutable strings of characters.
130 class FlatString
131 super FlatText
132 super String
133
134 # Index at which `self` begins in `_items`, inclusively
135 redef var first_byte is noinit
136
137 # Index at which `self` ends in `_items`, inclusively
138 redef var last_byte is noinit
139
140 redef var chars = new FlatStringCharView(self) is lazy
141
142 redef var bytes = new FlatStringByteView(self) is lazy
143
144 redef var length is lazy do
145 if _bytelen == 0 then return 0
146 var st = _first_byte
147 var its = _items
148 var ln = 0
149 var lst = _last_byte
150 while st <= lst do
151 st += its.length_of_char_at(st)
152 ln += 1
153 end
154 return ln
155 end
156
157 redef fun reversed
158 do
159 var b = new FlatBuffer.with_capacity(_bytelen + 1)
160 for i in [length - 1 .. 0].step(-1) do
161 b.add self[i]
162 end
163 var s = b.to_s.as(FlatString)
164 s.length = self.length
165 return s
166 end
167
168 redef fun fast_cstring do return _items.fast_cstring(_first_byte)
169
170 redef fun substring(from, count)
171 do
172 assert count >= 0
173
174 if from < 0 then
175 count += from
176 if count < 0 then count = 0
177 from = 0
178 end
179
180 if (count + from) > length then count = length - from
181 if count <= 0 then return ""
182 var end_index = from + count - 1
183
184 var bytefrom = char_to_byte_index(from)
185 var byteto = char_to_byte_index(end_index)
186 var its = _items
187 byteto += its.length_of_char_at(byteto) - 1
188
189 var s = new FlatString.full(its, byteto - bytefrom + 1, bytefrom, byteto, count)
190 return s
191 end
192
193 redef fun empty do return "".as(FlatString)
194
195 redef fun to_upper
196 do
197 var outstr = new FlatBuffer.with_capacity(self._bytelen + 1)
198
199 var mylen = length
200 var pos = 0
201
202 while pos < mylen do
203 outstr.add(chars[pos].to_upper)
204 pos += 1
205 end
206
207 return outstr.to_s
208 end
209
210 redef fun to_lower
211 do
212 var outstr = new FlatBuffer.with_capacity(self._bytelen + 1)
213
214 var mylen = length
215 var pos = 0
216
217 while pos < mylen do
218 outstr.add(chars[pos].to_lower)
219 pos += 1
220 end
221
222 return outstr.to_s
223 end
224
225 redef fun output
226 do
227 for i in chars do i.output
228 end
229
230 ##################################################
231 # String Specific Methods #
232 ##################################################
233
234 # Low-level creation of a new string with minimal data.
235 #
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)
239 do
240 self._items = items
241 self._bytelen = bytelen
242 _first_byte = from
243 _last_byte = to
244 _bytepos = from
245 end
246
247 # Low-level creation of a new string with all the data.
248 #
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)
252 do
253 self._items = items
254 self.length = length
255 self._bytelen = bytelen
256 _first_byte = from
257 _last_byte = to
258 _bytepos = from
259 end
260
261 redef fun to_cstring do
262 if real_items != null then return real_items.as(not null)
263 var blen = _bytelen
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
268 return new_items
269 end
270
271 redef fun ==(other)
272 do
273 if not other isa FlatString then return super
274
275 if self.object_id == other.object_id then return true
276
277 var my_length = _bytelen
278
279 if other._bytelen != my_length then return false
280
281 var my_index = _first_byte
282 var its_index = other._first_byte
283
284 var last_iteration = my_index + my_length
285
286 var its_items = other._items
287 var my_items = self._items
288
289 while my_index < last_iteration do
290 if my_items[my_index] != its_items[its_index] then return false
291 my_index += 1
292 its_index += 1
293 end
294
295 return true
296 end
297
298 redef fun <(other)
299 do
300 if not other isa FlatString then return super
301
302 if self.object_id == other.object_id then return false
303
304 var my_length = self._bytelen
305 var its_length = other._bytelen
306
307 var max = if my_length < its_length then my_length else its_length
308
309 var myits = self.bytes
310 var itsits = other.bytes
311
312 for i in [0 .. max[ do
313 var my_curr_char = myits[i]
314 var its_curr_char = itsits[i]
315
316 if my_curr_char != its_curr_char then
317 if my_curr_char < its_curr_char then return true
318 return false
319 end
320 end
321
322 return my_length < its_length
323 end
324
325 redef fun +(o) do
326 var s = o.to_s
327 var slen = s.bytelen
328 var mlen = _bytelen
329 var nlen = mlen + slen
330 var mits = _items
331 var mifrom = _first_byte
332 if s isa FlatText then
333 var sits = s._items
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)
339 else
340 abort
341 end
342 end
343
344 redef fun *(i) do
345 var mybtlen = _bytelen
346 var new_bytelen = mybtlen * i
347 var mylen = length
348 var newlen = mylen * i
349 var its = _items
350 var fb = _first_byte
351 var ns = new NativeString(new_bytelen + 1)
352 ns[new_bytelen] = 0u8
353 var offset = 0
354 while i > 0 do
355 its.copy_to(ns, mybtlen, fb, offset)
356 offset += mybtlen
357 i -= 1
358 end
359 return new FlatString.full(ns, new_bytelen, 0, new_bytelen - 1, newlen)
360 end
361
362
363 redef fun hash
364 do
365 if hash_cache == null then
366 # djb2 hash algorithm
367 var h = 5381
368 var i = _first_byte
369
370 var my_items = _items
371 var max = _last_byte
372
373 while i <= max do
374 h = (h << 5) + h + my_items[i].to_i
375 i += 1
376 end
377
378 hash_cache = h
379 end
380
381 return hash_cache.as(not null)
382 end
383
384 redef fun substrings do return new FlatSubstringsIter(self)
385 end
386
387 private class FlatStringCharReverseIterator
388 super IndexedIterator[Char]
389
390 var target: FlatString
391
392 var curr_pos: Int
393
394 init with_pos(tgt: FlatString, pos: Int)
395 do
396 init(tgt, pos)
397 end
398
399 redef fun is_ok do return curr_pos >= 0
400
401 redef fun item do return target[curr_pos]
402
403 redef fun next do curr_pos -= 1
404
405 redef fun index do return curr_pos
406
407 end
408
409 private class FlatStringCharIterator
410 super IndexedIterator[Char]
411
412 var target: FlatString
413
414 var max: Int
415
416 var curr_pos: Int
417
418 init with_pos(tgt: FlatString, pos: Int)
419 do
420 init(tgt, tgt.length - 1, pos)
421 end
422
423 redef fun is_ok do return curr_pos <= max
424
425 redef fun item do return target[curr_pos]
426
427 redef fun next do curr_pos += 1
428
429 redef fun index do return curr_pos
430
431 end
432
433 private class FlatStringCharView
434 super StringCharView
435
436 redef type SELFTYPE: FlatString
437
438 redef fun [](index) do return target[index]
439
440 redef fun iterator_from(start) do return new FlatStringCharIterator.with_pos(target, start)
441
442 redef fun reverse_iterator_from(start) do return new FlatStringCharReverseIterator.with_pos(target, start)
443
444 end
445
446 private class FlatStringByteReverseIterator
447 super IndexedIterator[Byte]
448
449 var target: FlatString
450
451 var target_items: NativeString
452
453 var curr_pos: Int
454
455 init with_pos(tgt: FlatString, pos: Int)
456 do
457 init(tgt, tgt._items, pos + tgt._first_byte)
458 end
459
460 redef fun is_ok do return curr_pos >= target._first_byte
461
462 redef fun item do return target_items[curr_pos]
463
464 redef fun next do curr_pos -= 1
465
466 redef fun index do return curr_pos - target._first_byte
467
468 end
469
470 private class FlatStringByteIterator
471 super IndexedIterator[Byte]
472
473 var target: FlatString
474
475 var target_items: NativeString
476
477 var curr_pos: Int
478
479 init with_pos(tgt: FlatString, pos: Int)
480 do
481 init(tgt, tgt._items, pos + tgt._first_byte)
482 end
483
484 redef fun is_ok do return curr_pos <= target._last_byte
485
486 redef fun item do return target_items[curr_pos]
487
488 redef fun next do curr_pos += 1
489
490 redef fun index do return curr_pos - target._first_byte
491
492 end
493
494 private class FlatStringByteView
495 super StringByteView
496
497 redef type SELFTYPE: FlatString
498
499 redef fun [](index)
500 do
501 # Check that the index (+ _first_byte) is not larger than _last_byte
502 # In other terms, if the index is valid
503 assert index >= 0
504 var target = self.target
505 var ind = index + target._first_byte
506 assert ind <= target._last_byte
507 return target._items[ind]
508 end
509
510 redef fun iterator_from(start) do return new FlatStringByteIterator.with_pos(target, start)
511
512 redef fun reverse_iterator_from(start) do return new FlatStringByteReverseIterator.with_pos(target, start)
513
514 end
515
516 redef class Buffer
517 redef new do return new FlatBuffer
518
519 redef new with_cap(i) do return new FlatBuffer.with_capacity(i)
520 end
521
522 # Mutable strings of characters.
523 class FlatBuffer
524 super FlatText
525 super Buffer
526
527 redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
528
529 redef var bytes = new FlatBufferByteView(self) is lazy
530
531 redef var length = 0
532
533 private var char_cache: Int = -1
534
535 private var byte_cache: Int = -1
536
537 private var capacity = 0
538
539 redef fun fast_cstring do return _items.fast_cstring(0)
540
541 redef fun substrings do return new FlatSubstringsIter(self)
542
543 # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
544 #
545 # This happens when an operation modifies the current `Buffer` and
546 # the Copy-On-Write flag `written` is set at true.
547 private fun reset do
548 var nns = new NativeString(capacity)
549 if _bytelen != 0 then _items.copy_to(nns, _bytelen, 0, 0)
550 _items = nns
551 written = false
552 end
553
554 # Shifts the content of the buffer by `len` bytes to the right, starting at byte `from`
555 #
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
558 var oit = _items
559 var nit = _items
560 var bt = _bytelen
561 if bt + len > capacity then
562 capacity = capacity * 2 + 2
563 nit = new NativeString(capacity)
564 oit.copy_to(nit, 0, 0, from)
565 end
566 oit.copy_to(nit, bt - from, from, from + len)
567 end
568
569 # Shifts the content of the buffer by `len` bytes to the left, starting at `from`
570 #
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
573 var it = _items
574 it.copy_to(it, _bytelen - from, from, from - len)
575 end
576
577 redef fun []=(index, item)
578 do
579 assert index >= 0 and index <= length
580 if written then reset
581 is_dirty = true
582 if index == length then
583 add item
584 return
585 end
586 var it = _items
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)
596 end
597 _bytelen += size_diff
598 bytepos += size_diff
599 it.set_char_at(ip, item)
600 end
601
602 redef fun add(c)
603 do
604 if written then reset
605 is_dirty = true
606 var clen = c.u8char_len
607 var bt = _bytelen
608 enlarge(bt + clen)
609 _items.set_char_at(bt, c)
610 _bytelen += clen
611 length += 1
612 end
613
614 redef fun clear do
615 is_dirty = true
616 if written then reset
617 _bytelen = 0
618 length = 0
619 end
620
621 redef fun empty do return new Buffer
622
623 redef fun enlarge(cap)
624 do
625 var c = capacity
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`
630 written = false
631 var bln = _bytelen
632 var a = new NativeString(c+1)
633 if bln > 0 then
634 var it = _items
635 if bln > 0 then it.copy_to(a, bln, 0, 0)
636 end
637 _items = a
638 capacity = c
639 end
640
641 redef fun to_s
642 do
643 written = true
644 var bln = _bytelen
645 if bln == 0 then _items = new NativeString(1)
646 return new FlatString.full(_items, bln, 0, bln - 1, length)
647 end
648
649 redef fun to_cstring
650 do
651 if is_dirty then
652 var bln = _bytelen
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
657 is_dirty = false
658 end
659 return real_items.as(not null)
660 end
661
662 # Create a new empty string.
663 init do end
664
665 # Low-level creation a new buffer with given data.
666 #
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.
669 #
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)
673 do
674 self._items = items
675 self.capacity = capacity
676 self._bytelen = bytelen
677 self.length = length
678 end
679
680 # Create a new string copied from `s`.
681 init from(s: Text)
682 do
683 _items = new NativeString(s.bytelen)
684 if s isa FlatText then
685 _items = s._items
686 else
687 for i in substrings do i.as(FlatString)._items.copy_to(_items, i._bytelen, 0, 0)
688 end
689 _bytelen = s.bytelen
690 length = s.length
691 _capacity = _bytelen
692 written = true
693 end
694
695 # Create a new empty string with a given capacity.
696 init with_capacity(cap: Int)
697 do
698 assert cap >= 0
699 _items = new NativeString(cap + 1)
700 capacity = cap
701 _bytelen = 0
702 end
703
704 redef fun append(s)
705 do
706 if s.is_empty then return
707 is_dirty = true
708 var sl = s.bytelen
709 var nln = _bytelen + sl
710 enlarge(nln)
711 if s isa FlatText then
712 s._items.copy_to(_items, sl, s.first_byte, _bytelen)
713 else
714 for i in s.substrings do append i
715 return
716 end
717 _bytelen = nln
718 length += s.length
719 end
720
721 # Copies the content of self in `dest`
722 fun copy(start: Int, len: Int, dest: Buffer, new_start: Int)
723 do
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]
728 end
729 end
730
731 redef fun substring(from, count)
732 do
733 assert count >= 0
734 if from < 0 then from = 0
735 if (from + count) > length then count = length - from
736 if count != 0 then
737 var its = _items
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)
745 else
746 return new Buffer
747 end
748 end
749
750 redef fun reverse
751 do
752 written = false
753 var ns = new FlatBuffer.with_capacity(capacity)
754 for i in chars.reverse_iterator do ns.add i
755 _items = ns._items
756 end
757
758 redef fun times(repeats)
759 do
760 var bln = _bytelen
761 var x = new FlatString.full(_items, bln, 0, bln - 1, length)
762 for i in [1 .. repeats[ do
763 append(x)
764 end
765 end
766
767 redef fun upper
768 do
769 if written then reset
770 for i in [0 .. length[ do self[i] = self[i].to_upper
771 end
772
773 redef fun lower
774 do
775 if written then reset
776 for i in [0 .. length[ do self[i] = self[i].to_lower
777 end
778 end
779
780 private class FlatBufferByteReverseIterator
781 super IndexedIterator[Byte]
782
783 var target: FlatBuffer
784
785 var target_items: NativeString
786
787 var curr_pos: Int
788
789 init with_pos(tgt: FlatBuffer, pos: Int)
790 do
791 init(tgt, tgt._items, pos)
792 end
793
794 redef fun index do return curr_pos
795
796 redef fun is_ok do return curr_pos >= 0
797
798 redef fun item do return target_items[curr_pos]
799
800 redef fun next do curr_pos -= 1
801
802 end
803
804 private class FlatBufferByteView
805 super BufferByteView
806
807 redef type SELFTYPE: FlatBuffer
808
809 redef fun [](index) do return target._items[index]
810
811 redef fun iterator_from(pos) do return new FlatBufferByteIterator.with_pos(target, pos)
812
813 redef fun reverse_iterator_from(pos) do return new FlatBufferByteReverseIterator.with_pos(target, pos)
814
815 end
816
817 private class FlatBufferByteIterator
818 super IndexedIterator[Byte]
819
820 var target: FlatBuffer
821
822 var target_items: NativeString
823
824 var curr_pos: Int
825
826 init with_pos(tgt: FlatBuffer, pos: Int)
827 do
828 init(tgt, tgt._items, pos)
829 end
830
831 redef fun index do return curr_pos
832
833 redef fun is_ok do return curr_pos < target._bytelen
834
835 redef fun item do return target_items[curr_pos]
836
837 redef fun next do curr_pos += 1
838
839 end
840
841 private class FlatBufferCharReverseIterator
842 super IndexedIterator[Char]
843
844 var target: FlatBuffer
845
846 var curr_pos: Int
847
848 init with_pos(tgt: FlatBuffer, pos: Int)
849 do
850 init(tgt, pos)
851 end
852
853 redef fun index do return curr_pos
854
855 redef fun is_ok do return curr_pos >= 0
856
857 redef fun item do return target[curr_pos]
858
859 redef fun next do curr_pos -= 1
860
861 end
862
863 private class FlatBufferCharView
864 super BufferCharView
865
866 redef type SELFTYPE: FlatBuffer
867
868 redef fun [](index) do return target[index]
869
870 redef fun []=(index, item)
871 do
872 assert index >= 0 and index <= length
873 if index == length then
874 add(item)
875 return
876 end
877 target[index] = item
878 end
879
880 redef fun push(c)
881 do
882 target.add(c)
883 end
884
885 redef fun add(c)
886 do
887 target.add(c)
888 end
889
890 fun enlarge(cap: Int)
891 do
892 target.enlarge(cap)
893 end
894
895 redef fun append(s)
896 do
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
900 end
901
902 redef fun iterator_from(pos) do return new FlatBufferCharIterator.with_pos(target, pos)
903
904 redef fun reverse_iterator_from(pos) do return new FlatBufferCharReverseIterator.with_pos(target, pos)
905
906 end
907
908 private class FlatBufferCharIterator
909 super IndexedIterator[Char]
910
911 var target: FlatBuffer
912
913 var max: Int
914
915 var curr_pos: Int
916
917 init with_pos(tgt: FlatBuffer, pos: Int)
918 do
919 init(tgt, tgt.length - 1, pos)
920 end
921
922 redef fun index do return curr_pos
923
924 redef fun is_ok do return curr_pos <= max
925
926 redef fun item do return target[curr_pos]
927
928 redef fun next do curr_pos += 1
929
930 end
931
932 redef class NativeString
933 redef fun to_s
934 do
935 return to_s_with_length(cstring_length)
936 end
937
938 # Returns `self` as a String of `length`.
939 redef fun to_s_with_length(length): FlatString
940 do
941 assert length >= 0
942 var str = new FlatString.with_infos(self, length, 0, length - 1)
943 return str
944 end
945
946 redef fun to_s_full(bytelen, unilen) do
947 return new FlatString.full(self, bytelen, 0, bytelen - 1, unilen)
948 end
949
950 # Returns `self` as a new String.
951 redef fun to_s_with_copy: FlatString
952 do
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
959 return str
960 end
961
962 # Sets the next bytes at position `pos` to the value of `c`, encoded in UTF-8
963 #
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)
968 end
969
970 private fun native_set_char(pos: Int, c: Char, ln: Int) `{
971 char* dst = self + pos;
972 switch(ln){
973 case 1:
974 dst[0] = c;
975 break;
976 case 2:
977 dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
978 dst[1] = 0x80 | (c & 0x3F);
979 break;
980 case 3:
981 dst[0] = 0xE0 | ((c & 0xF000) >> 12);
982 dst[1] = 0x80 | ((c & 0xFC0) >> 6);
983 dst[2] = 0x80 | (c & 0x3F);
984 break;
985 case 4:
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);
990 break;
991 }
992 `}
993 end
994
995 redef class Int
996 redef fun to_base(base, signed)
997 do
998 var l = digit_count(base)
999 var s = new FlatBuffer.from(" " * l)
1000 fill_buffer(s, base, signed)
1001 return s.to_s
1002 end
1003
1004 # return displayable int in base 10 and signed
1005 #
1006 # assert 1.to_s == "1"
1007 # assert (-123).to_s == "-123"
1008 redef fun to_s do
1009 # Fast case for common numbers
1010 if self == 0 then return "0"
1011 if self == 1 then return "1"
1012
1013 var nslen = int_to_s_len
1014 var ns = new NativeString(nslen + 1)
1015 ns[nslen] = 0u8
1016 native_int_to_s(ns, nslen + 1)
1017 return new FlatString.full(ns, nslen, 0, nslen - 1, nslen)
1018 end
1019 end
1020
1021 redef class Array[E]
1022
1023 # Fast implementation
1024 redef fun plain_to_s
1025 do
1026 var l = length
1027 if l == 0 then return ""
1028 if l == 1 then if self[0] == null then return "" else return self[0].to_s
1029 var its = _items
1030 var na = new NativeArray[String](l)
1031 var i = 0
1032 var sl = 0
1033 var mypos = 0
1034 while i < l do
1035 var itsi = its[i]
1036 if itsi == null then
1037 i += 1
1038 continue
1039 end
1040 var tmp = itsi.to_s
1041 sl += tmp.bytelen
1042 na[mypos] = tmp
1043 i += 1
1044 mypos += 1
1045 end
1046 var ns = new NativeString(sl + 1)
1047 ns[sl] = 0u8
1048 i = 0
1049 var off = 0
1050 while i < mypos do
1051 var tmp = na[i]
1052 if tmp isa FlatString then
1053 var tpl = tmp._bytelen
1054 tmp._items.copy_to(ns, tpl, tmp._first_byte, off)
1055 off += tpl
1056 else
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)
1061 off += slen
1062 end
1063 end
1064 i += 1
1065 end
1066 return ns.to_s_with_length(sl)
1067 end
1068 end
1069
1070 redef class NativeArray[E]
1071 redef fun native_to_s do
1072 assert self isa NativeArray[String]
1073 var l = length
1074 var na = self
1075 var i = 0
1076 var sl = 0
1077 var mypos = 0
1078 while i < l do
1079 sl += na[i].bytelen
1080 i += 1
1081 mypos += 1
1082 end
1083 var ns = new NativeString(sl + 1)
1084 ns[sl] = 0u8
1085 i = 0
1086 var off = 0
1087 while i < mypos do
1088 var tmp = na[i]
1089 if tmp isa FlatString then
1090 var tpl = tmp._bytelen
1091 tmp._items.copy_to(ns, tpl, tmp._first_byte, off)
1092 off += tpl
1093 else
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)
1098 off += slen
1099 end
1100 end
1101 i += 1
1102 end
1103 return ns.to_s_with_length(sl)
1104 end
1105 end
1106
1107 redef class Map[K,V]
1108 redef fun join(sep, couple_sep)
1109 do
1110 if is_empty then return ""
1111
1112 var s = new Buffer # Result
1113
1114 # Concat first item
1115 var i = iterator
1116 var k = i.key
1117 var e = i.item
1118 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1119
1120 # Concat other _items
1121 i.next
1122 while i.is_ok do
1123 s.append(sep)
1124 k = i.key
1125 e = i.item
1126 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1127 i.next
1128 end
1129 return s.to_s
1130 end
1131 end