lib/core/text: Removed now useless byte_to_char_index function
[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 # By escaping `self` to C, how many more bytes will be needed ?
89 #
90 # This enables a double-optimization in `escape_to_c` since if this
91 # method returns 0, then `self` does not need escaping and can be
92 # returned as-is
93 protected fun chars_to_escape_to_c: Int do
94 var its = _items
95 var max = last_byte
96 var pos = first_byte
97 var req_esc = 0
98 while pos <= max do
99 var c = its[pos]
100 if c == 0x0Au8 then
101 req_esc += 1
102 else if c == 0x09u8 then
103 req_esc += 1
104 else if c == 0x22u8 then
105 req_esc += 1
106 else if c == 0x27u8 then
107 req_esc += 1
108 else if c == 0x5Cu8 then
109 req_esc += 1
110 else if c < 32u8 then
111 req_esc += 3
112 end
113 pos += 1
114 end
115 return req_esc
116 end
117
118 redef fun escape_to_c do
119 var ln_extra = chars_to_escape_to_c
120 if ln_extra == 0 then return self.to_s
121 var its = _items
122 var max = last_byte
123 var nlen = _bytelen + ln_extra
124 var nns = new NativeString(nlen)
125 var pos = first_byte
126 var opos = 0
127 while pos <= max do
128 var c = its[pos]
129 # Special codes:
130 #
131 # Any byte with value < 32 is a control character
132 # All their uses will be replaced by their octal
133 # value in C.
134 #
135 # There are two exceptions however:
136 #
137 # * 0x09 => \t
138 # * 0x0A => \n
139 #
140 # Aside from the code points above, the following are:
141 #
142 # * 0x22 => \"
143 # * 0x27 => \'
144 # * 0x5C => \\
145 if c == 0x09u8 then
146 nns[opos] = 0x5Cu8
147 nns[opos + 1] = 0x74u8
148 opos += 2
149 else if c == 0x0Au8 then
150 nns[opos] = 0x5Cu8
151 nns[opos + 1] = 0x6Eu8
152 opos += 2
153 else if c == 0x22u8 then
154 nns[opos] = 0x5Cu8
155 nns[opos + 1] = 0x22u8
156 opos += 2
157 else if c == 0x27u8 then
158 nns[opos] = 0x5Cu8
159 nns[opos + 1] = 0x27u8
160 opos += 2
161 else if c == 0x5Cu8 then
162 nns[opos] = 0x5Cu8
163 nns[opos + 1] = 0x5Cu8
164 opos += 2
165 else if c < 32u8 then
166 nns[opos] = 0x5Cu8
167 nns[opos + 1] = 0x30u8
168 nns[opos + 2] = ((c & 0x38u8) >> 3) + 0x30u8
169 nns[opos + 3] = (c & 0x07u8) + 0x30u8
170 opos += 4
171 else
172 nns[opos] = c
173 opos += 1
174 end
175 pos += 1
176 end
177 return nns.to_s_with_length(nlen)
178 end
179
180 redef fun [](index) do return _items.char_at(char_to_byte_index(index))
181 end
182
183 # Immutable strings of characters.
184 class FlatString
185 super FlatText
186 super String
187
188 # Index at which `self` begins in `_items`, inclusively
189 redef var first_byte is noinit
190
191 # Index at which `self` ends in `_items`, inclusively
192 redef var last_byte is noinit
193
194 redef var chars = new FlatStringCharView(self) is lazy
195
196 redef var bytes = new FlatStringByteView(self) is lazy
197
198 redef var length is lazy do
199 if _bytelen == 0 then return 0
200 var st = _first_byte
201 var its = _items
202 var ln = 0
203 var lst = _last_byte
204 while st <= lst do
205 st += its.length_of_char_at(st)
206 ln += 1
207 end
208 return ln
209 end
210
211 redef fun reversed
212 do
213 var b = new FlatBuffer.with_capacity(_bytelen + 1)
214 for i in [length - 1 .. 0].step(-1) do
215 b.add self[i]
216 end
217 var s = b.to_s.as(FlatString)
218 s.length = self.length
219 return s
220 end
221
222 redef fun fast_cstring do return _items.fast_cstring(_first_byte)
223
224 redef fun substring(from, count)
225 do
226 assert count >= 0
227
228 if from < 0 then
229 count += from
230 if count < 0 then count = 0
231 from = 0
232 end
233
234 if (count + from) > length then count = length - from
235 if count <= 0 then return ""
236 var end_index = from + count - 1
237
238 var bytefrom = char_to_byte_index(from)
239 var byteto = char_to_byte_index(end_index)
240 var its = _items
241 byteto += its.length_of_char_at(byteto) - 1
242
243 var s = new FlatString.full(its, byteto - bytefrom + 1, bytefrom, byteto, count)
244 return s
245 end
246
247 redef fun empty do return "".as(FlatString)
248
249 redef fun to_upper
250 do
251 var outstr = new FlatBuffer.with_capacity(self._bytelen + 1)
252
253 var mylen = length
254 var pos = 0
255
256 while pos < mylen do
257 outstr.add(chars[pos].to_upper)
258 pos += 1
259 end
260
261 return outstr.to_s
262 end
263
264 redef fun to_lower
265 do
266 var outstr = new FlatBuffer.with_capacity(self._bytelen + 1)
267
268 var mylen = length
269 var pos = 0
270
271 while pos < mylen do
272 outstr.add(chars[pos].to_lower)
273 pos += 1
274 end
275
276 return outstr.to_s
277 end
278
279 redef fun output
280 do
281 for i in chars do i.output
282 end
283
284 ##################################################
285 # String Specific Methods #
286 ##################################################
287
288 # Low-level creation of a new string with minimal data.
289 #
290 # `_items` will be used as is, without copy, to retrieve the characters of the string.
291 # Aliasing issues is the responsibility of the caller.
292 private init with_infos(items: NativeString, bytelen, from, to: Int)
293 do
294 self._items = items
295 self._bytelen = bytelen
296 _first_byte = from
297 _last_byte = to
298 _bytepos = from
299 end
300
301 # Low-level creation of a new string with all the data.
302 #
303 # `_items` will be used as is, without copy, to retrieve the characters of the string.
304 # Aliasing issues is the responsibility of the caller.
305 private init full(items: NativeString, bytelen, from, to, length: Int)
306 do
307 self._items = items
308 self.length = length
309 self._bytelen = bytelen
310 _first_byte = from
311 _last_byte = to
312 _bytepos = from
313 end
314
315 redef fun to_cstring do
316 if real_items != null then return real_items.as(not null)
317 var blen = _bytelen
318 var new_items = new NativeString(blen + 1)
319 _items.copy_to(new_items, blen, _first_byte, 0)
320 new_items[blen] = 0u8
321 real_items = new_items
322 return new_items
323 end
324
325 redef fun ==(other)
326 do
327 if not other isa FlatString then return super
328
329 if self.object_id == other.object_id then return true
330
331 var my_length = _bytelen
332
333 if other._bytelen != my_length then return false
334
335 var my_index = _first_byte
336 var its_index = other._first_byte
337
338 var last_iteration = my_index + my_length
339
340 var its_items = other._items
341 var my_items = self._items
342
343 while my_index < last_iteration do
344 if my_items[my_index] != its_items[its_index] then return false
345 my_index += 1
346 its_index += 1
347 end
348
349 return true
350 end
351
352 redef fun <(other)
353 do
354 if not other isa FlatString then return super
355
356 if self.object_id == other.object_id then return false
357
358 var my_length = self._bytelen
359 var its_length = other._bytelen
360
361 var max = if my_length < its_length then my_length else its_length
362
363 var myits = self.bytes
364 var itsits = other.bytes
365
366 for i in [0 .. max[ do
367 var my_curr_char = myits[i]
368 var its_curr_char = itsits[i]
369
370 if my_curr_char != its_curr_char then
371 if my_curr_char < its_curr_char then return true
372 return false
373 end
374 end
375
376 return my_length < its_length
377 end
378
379 redef fun +(o) do
380 var s = o.to_s
381 var slen = s.bytelen
382 var mlen = _bytelen
383 var nlen = mlen + slen
384 var mits = _items
385 var mifrom = _first_byte
386 if s isa FlatText then
387 var sits = s._items
388 var sifrom = s.first_byte
389 var ns = new NativeString(nlen + 1)
390 mits.copy_to(ns, mlen, mifrom, 0)
391 sits.copy_to(ns, slen, sifrom, mlen)
392 return new FlatString.full(ns, nlen, 0, nlen - 1, length + o.length)
393 else
394 abort
395 end
396 end
397
398 redef fun *(i) do
399 var mybtlen = _bytelen
400 var new_bytelen = mybtlen * i
401 var mylen = length
402 var newlen = mylen * i
403 var its = _items
404 var fb = _first_byte
405 var ns = new NativeString(new_bytelen + 1)
406 ns[new_bytelen] = 0u8
407 var offset = 0
408 while i > 0 do
409 its.copy_to(ns, mybtlen, fb, offset)
410 offset += mybtlen
411 i -= 1
412 end
413 return new FlatString.full(ns, new_bytelen, 0, new_bytelen - 1, newlen)
414 end
415
416
417 redef fun hash
418 do
419 if hash_cache == null then
420 # djb2 hash algorithm
421 var h = 5381
422 var i = _first_byte
423
424 var my_items = _items
425 var max = _last_byte
426
427 while i <= max do
428 h = (h << 5) + h + my_items[i].to_i
429 i += 1
430 end
431
432 hash_cache = h
433 end
434
435 return hash_cache.as(not null)
436 end
437
438 redef fun substrings do return new FlatSubstringsIter(self)
439 end
440
441 private class FlatStringCharReverseIterator
442 super IndexedIterator[Char]
443
444 var target: FlatString
445
446 var curr_pos: Int
447
448 init with_pos(tgt: FlatString, pos: Int)
449 do
450 init(tgt, pos)
451 end
452
453 redef fun is_ok do return curr_pos >= 0
454
455 redef fun item do return target[curr_pos]
456
457 redef fun next do curr_pos -= 1
458
459 redef fun index do return curr_pos
460
461 end
462
463 private class FlatStringCharIterator
464 super IndexedIterator[Char]
465
466 var target: FlatString
467
468 var max: Int
469
470 var curr_pos: Int
471
472 init with_pos(tgt: FlatString, pos: Int)
473 do
474 init(tgt, tgt.length - 1, pos)
475 end
476
477 redef fun is_ok do return curr_pos <= max
478
479 redef fun item do return target[curr_pos]
480
481 redef fun next do curr_pos += 1
482
483 redef fun index do return curr_pos
484
485 end
486
487 private class FlatStringCharView
488 super StringCharView
489
490 redef type SELFTYPE: FlatString
491
492 redef fun [](index) do return target[index]
493
494 redef fun iterator_from(start) do return new FlatStringCharIterator.with_pos(target, start)
495
496 redef fun reverse_iterator_from(start) do return new FlatStringCharReverseIterator.with_pos(target, start)
497
498 end
499
500 private class FlatStringByteReverseIterator
501 super IndexedIterator[Byte]
502
503 var target: FlatString
504
505 var target_items: NativeString
506
507 var curr_pos: Int
508
509 init with_pos(tgt: FlatString, pos: Int)
510 do
511 init(tgt, tgt._items, pos + tgt._first_byte)
512 end
513
514 redef fun is_ok do return curr_pos >= target._first_byte
515
516 redef fun item do return target_items[curr_pos]
517
518 redef fun next do curr_pos -= 1
519
520 redef fun index do return curr_pos - target._first_byte
521
522 end
523
524 private class FlatStringByteIterator
525 super IndexedIterator[Byte]
526
527 var target: FlatString
528
529 var target_items: NativeString
530
531 var curr_pos: Int
532
533 init with_pos(tgt: FlatString, pos: Int)
534 do
535 init(tgt, tgt._items, pos + tgt._first_byte)
536 end
537
538 redef fun is_ok do return curr_pos <= target._last_byte
539
540 redef fun item do return target_items[curr_pos]
541
542 redef fun next do curr_pos += 1
543
544 redef fun index do return curr_pos - target._first_byte
545
546 end
547
548 private class FlatStringByteView
549 super StringByteView
550
551 redef type SELFTYPE: FlatString
552
553 redef fun [](index)
554 do
555 # Check that the index (+ _first_byte) is not larger than _last_byte
556 # In other terms, if the index is valid
557 assert index >= 0
558 var target = self.target
559 var ind = index + target._first_byte
560 assert ind <= target._last_byte
561 return target._items[ind]
562 end
563
564 redef fun iterator_from(start) do return new FlatStringByteIterator.with_pos(target, start)
565
566 redef fun reverse_iterator_from(start) do return new FlatStringByteReverseIterator.with_pos(target, start)
567
568 end
569
570 redef class Buffer
571 redef new do return new FlatBuffer
572
573 redef new with_cap(i) do return new FlatBuffer.with_capacity(i)
574 end
575
576 # Mutable strings of characters.
577 class FlatBuffer
578 super FlatText
579 super Buffer
580
581 redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
582
583 redef var bytes = new FlatBufferByteView(self) is lazy
584
585 redef var length = 0
586
587 private var char_cache: Int = -1
588
589 private var byte_cache: Int = -1
590
591 private var capacity = 0
592
593 redef fun fast_cstring do return _items.fast_cstring(0)
594
595 redef fun substrings do return new FlatSubstringsIter(self)
596
597 # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
598 #
599 # This happens when an operation modifies the current `Buffer` and
600 # the Copy-On-Write flag `written` is set at true.
601 private fun reset do
602 var nns = new NativeString(capacity)
603 if _bytelen != 0 then _items.copy_to(nns, _bytelen, 0, 0)
604 _items = nns
605 written = false
606 end
607
608 # Shifts the content of the buffer by `len` bytes to the right, starting at byte `from`
609 #
610 # Internal only, does not modify _bytelen or length, this is the caller's responsability
611 private fun rshift_bytes(from: Int, len: Int) do
612 var oit = _items
613 var nit = _items
614 var bt = _bytelen
615 if bt + len > capacity then
616 capacity = capacity * 2 + 2
617 nit = new NativeString(capacity)
618 oit.copy_to(nit, 0, 0, from)
619 end
620 oit.copy_to(nit, bt - from, from, from + len)
621 end
622
623 # Shifts the content of the buffer by `len` bytes to the left, starting at `from`
624 #
625 # Internal only, does not modify _bytelen or length, this is the caller's responsability
626 private fun lshift_bytes(from: Int, len: Int) do
627 var it = _items
628 it.copy_to(it, _bytelen - from, from, from - len)
629 end
630
631 redef fun []=(index, item)
632 do
633 assert index >= 0 and index <= length
634 if written then reset
635 is_dirty = true
636 if index == length then
637 add item
638 return
639 end
640 var it = _items
641 var ip = it.char_to_byte_index(index)
642 var c = it.char_at(ip)
643 var clen = c.u8char_len
644 var itemlen = item.u8char_len
645 var size_diff = itemlen - clen
646 if size_diff > 0 then
647 rshift_bytes(ip + clen, size_diff)
648 else if size_diff < 0 then
649 lshift_bytes(ip + clen, -size_diff)
650 end
651 _bytelen += size_diff
652 bytepos += size_diff
653 it.set_char_at(ip, item)
654 end
655
656 redef fun add(c)
657 do
658 if written then reset
659 is_dirty = true
660 var clen = c.u8char_len
661 var bt = _bytelen
662 enlarge(bt + clen)
663 _items.set_char_at(bt, c)
664 _bytelen += clen
665 length += 1
666 end
667
668 redef fun clear do
669 is_dirty = true
670 if written then reset
671 _bytelen = 0
672 length = 0
673 end
674
675 redef fun empty do return new Buffer
676
677 redef fun enlarge(cap)
678 do
679 var c = capacity
680 if cap <= c then return
681 while c <= cap do c = c * 2 + 2
682 # The COW flag can be set at false here, since
683 # it does a copy of the current `Buffer`
684 written = false
685 var bln = _bytelen
686 var a = new NativeString(c+1)
687 if bln > 0 then
688 var it = _items
689 if bln > 0 then it.copy_to(a, bln, 0, 0)
690 end
691 _items = a
692 capacity = c
693 end
694
695 redef fun to_s
696 do
697 written = true
698 var bln = _bytelen
699 if bln == 0 then _items = new NativeString(1)
700 return new FlatString.full(_items, bln, 0, bln - 1, length)
701 end
702
703 redef fun to_cstring
704 do
705 if is_dirty then
706 var bln = _bytelen
707 var new_native = new NativeString(bln + 1)
708 new_native[bln] = 0u8
709 if length > 0 then _items.copy_to(new_native, bln, 0, 0)
710 real_items = new_native
711 is_dirty = false
712 end
713 return real_items.as(not null)
714 end
715
716 # Create a new empty string.
717 init do end
718
719 # Low-level creation a new buffer with given data.
720 #
721 # `_items` will be used as is, without copy, to store the characters of the buffer.
722 # Aliasing issues is the responsibility of the caller.
723 #
724 # If `_items` is shared, `written` should be set to true after the creation
725 # so that a modification will do a copy-on-write.
726 private init with_infos(items: NativeString, capacity, bytelen, length: Int)
727 do
728 self._items = items
729 self.capacity = capacity
730 self._bytelen = bytelen
731 self.length = length
732 end
733
734 # Create a new string copied from `s`.
735 init from(s: Text)
736 do
737 _items = new NativeString(s.bytelen)
738 if s isa FlatText then
739 _items = s._items
740 else
741 for i in substrings do i.as(FlatString)._items.copy_to(_items, i._bytelen, 0, 0)
742 end
743 _bytelen = s.bytelen
744 length = s.length
745 _capacity = _bytelen
746 written = true
747 end
748
749 # Create a new empty string with a given capacity.
750 init with_capacity(cap: Int)
751 do
752 assert cap >= 0
753 _items = new NativeString(cap + 1)
754 capacity = cap
755 _bytelen = 0
756 end
757
758 redef fun append(s)
759 do
760 if s.is_empty then return
761 is_dirty = true
762 var sl = s.bytelen
763 var nln = _bytelen + sl
764 enlarge(nln)
765 if s isa FlatText then
766 s._items.copy_to(_items, sl, s.first_byte, _bytelen)
767 else
768 for i in s.substrings do append i
769 return
770 end
771 _bytelen = nln
772 length += s.length
773 end
774
775 # Copies the content of self in `dest`
776 fun copy(start: Int, len: Int, dest: Buffer, new_start: Int)
777 do
778 var self_chars = self.chars
779 var dest_chars = dest.chars
780 for i in [0..len-1] do
781 dest_chars[new_start+i] = self_chars[start+i]
782 end
783 end
784
785 redef fun substring(from, count)
786 do
787 assert count >= 0
788 if from < 0 then from = 0
789 if (from + count) > length then count = length - from
790 if count != 0 then
791 var its = _items
792 var bytefrom = its.char_to_byte_index(from)
793 var byteto = its.char_to_byte_index(count + from - 1)
794 byteto += its.char_at(byteto).u8char_len - 1
795 var byte_length = byteto - bytefrom + 1
796 var r_items = new NativeString(byte_length)
797 its.copy_to(r_items, byte_length, bytefrom, 0)
798 return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
799 else
800 return new Buffer
801 end
802 end
803
804 redef fun reverse
805 do
806 written = false
807 var ns = new FlatBuffer.with_capacity(capacity)
808 for i in chars.reverse_iterator do ns.add i
809 _items = ns._items
810 end
811
812 redef fun times(repeats)
813 do
814 var bln = _bytelen
815 var x = new FlatString.full(_items, bln, 0, bln - 1, length)
816 for i in [1 .. repeats[ do
817 append(x)
818 end
819 end
820
821 redef fun upper
822 do
823 if written then reset
824 for i in [0 .. length[ do self[i] = self[i].to_upper
825 end
826
827 redef fun lower
828 do
829 if written then reset
830 for i in [0 .. length[ do self[i] = self[i].to_lower
831 end
832 end
833
834 private class FlatBufferByteReverseIterator
835 super IndexedIterator[Byte]
836
837 var target: FlatBuffer
838
839 var target_items: NativeString
840
841 var curr_pos: Int
842
843 init with_pos(tgt: FlatBuffer, pos: Int)
844 do
845 init(tgt, tgt._items, pos)
846 end
847
848 redef fun index do return curr_pos
849
850 redef fun is_ok do return curr_pos >= 0
851
852 redef fun item do return target_items[curr_pos]
853
854 redef fun next do curr_pos -= 1
855
856 end
857
858 private class FlatBufferByteView
859 super BufferByteView
860
861 redef type SELFTYPE: FlatBuffer
862
863 redef fun [](index) do return target._items[index]
864
865 redef fun iterator_from(pos) do return new FlatBufferByteIterator.with_pos(target, pos)
866
867 redef fun reverse_iterator_from(pos) do return new FlatBufferByteReverseIterator.with_pos(target, pos)
868
869 end
870
871 private class FlatBufferByteIterator
872 super IndexedIterator[Byte]
873
874 var target: FlatBuffer
875
876 var target_items: NativeString
877
878 var curr_pos: Int
879
880 init with_pos(tgt: FlatBuffer, pos: Int)
881 do
882 init(tgt, tgt._items, pos)
883 end
884
885 redef fun index do return curr_pos
886
887 redef fun is_ok do return curr_pos < target._bytelen
888
889 redef fun item do return target_items[curr_pos]
890
891 redef fun next do curr_pos += 1
892
893 end
894
895 private class FlatBufferCharReverseIterator
896 super IndexedIterator[Char]
897
898 var target: FlatBuffer
899
900 var curr_pos: Int
901
902 init with_pos(tgt: FlatBuffer, pos: Int)
903 do
904 init(tgt, pos)
905 end
906
907 redef fun index do return curr_pos
908
909 redef fun is_ok do return curr_pos >= 0
910
911 redef fun item do return target[curr_pos]
912
913 redef fun next do curr_pos -= 1
914
915 end
916
917 private class FlatBufferCharView
918 super BufferCharView
919
920 redef type SELFTYPE: FlatBuffer
921
922 redef fun [](index) do return target[index]
923
924 redef fun []=(index, item)
925 do
926 assert index >= 0 and index <= length
927 if index == length then
928 add(item)
929 return
930 end
931 target[index] = item
932 end
933
934 redef fun push(c)
935 do
936 target.add(c)
937 end
938
939 redef fun add(c)
940 do
941 target.add(c)
942 end
943
944 fun enlarge(cap: Int)
945 do
946 target.enlarge(cap)
947 end
948
949 redef fun append(s)
950 do
951 var s_length = s.length
952 if target.capacity < s.length then enlarge(s_length + target.length)
953 for i in s do target.add i
954 end
955
956 redef fun iterator_from(pos) do return new FlatBufferCharIterator.with_pos(target, pos)
957
958 redef fun reverse_iterator_from(pos) do return new FlatBufferCharReverseIterator.with_pos(target, pos)
959
960 end
961
962 private class FlatBufferCharIterator
963 super IndexedIterator[Char]
964
965 var target: FlatBuffer
966
967 var max: Int
968
969 var curr_pos: Int
970
971 init with_pos(tgt: FlatBuffer, pos: Int)
972 do
973 init(tgt, tgt.length - 1, pos)
974 end
975
976 redef fun index do return curr_pos
977
978 redef fun is_ok do return curr_pos <= max
979
980 redef fun item do return target[curr_pos]
981
982 redef fun next do curr_pos += 1
983
984 end
985
986 redef class NativeString
987 redef fun to_s
988 do
989 return to_s_with_length(cstring_length)
990 end
991
992 # Returns `self` as a String of `length`.
993 redef fun to_s_with_length(length): FlatString
994 do
995 assert length >= 0
996 var str = new FlatString.with_infos(self, length, 0, length - 1)
997 return str
998 end
999
1000 redef fun to_s_full(bytelen, unilen) do
1001 return new FlatString.full(self, bytelen, 0, bytelen - 1, unilen)
1002 end
1003
1004 # Returns `self` as a new String.
1005 redef fun to_s_with_copy: FlatString
1006 do
1007 var length = cstring_length
1008 var new_self = new NativeString(length + 1)
1009 copy_to(new_self, length, 0, 0)
1010 var str = new FlatString.with_infos(new_self, length, 0, length - 1)
1011 new_self[length] = 0u8
1012 str.real_items = new_self
1013 return str
1014 end
1015
1016 # Sets the next bytes at position `pos` to the value of `c`, encoded in UTF-8
1017 #
1018 # Very unsafe, make sure to have room for this char prior to calling this function.
1019 private fun set_char_at(pos: Int, c: Char) do
1020 var ln = c.u8char_len
1021 native_set_char(pos, c, ln)
1022 end
1023
1024 private fun native_set_char(pos: Int, c: Char, ln: Int) `{
1025 char* dst = self + pos;
1026 switch(ln){
1027 case 1:
1028 dst[0] = c;
1029 break;
1030 case 2:
1031 dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
1032 dst[1] = 0x80 | (c & 0x3F);
1033 break;
1034 case 3:
1035 dst[0] = 0xE0 | ((c & 0xF000) >> 12);
1036 dst[1] = 0x80 | ((c & 0xFC0) >> 6);
1037 dst[2] = 0x80 | (c & 0x3F);
1038 break;
1039 case 4:
1040 dst[0] = 0xF0 | ((c & 0x1C0000) >> 18);
1041 dst[1] = 0x80 | ((c & 0x3F000) >> 12);
1042 dst[2] = 0x80 | ((c & 0xFC0) >> 6);
1043 dst[3] = 0x80 | (c & 0x3F);
1044 break;
1045 }
1046 `}
1047 end
1048
1049 redef class Int
1050 redef fun to_base(base, signed)
1051 do
1052 var l = digit_count(base)
1053 var s = new FlatBuffer.from(" " * l)
1054 fill_buffer(s, base, signed)
1055 return s.to_s
1056 end
1057
1058 # return displayable int in base 10 and signed
1059 #
1060 # assert 1.to_s == "1"
1061 # assert (-123).to_s == "-123"
1062 redef fun to_s do
1063 # Fast case for common numbers
1064 if self == 0 then return "0"
1065 if self == 1 then return "1"
1066
1067 var nslen = int_to_s_len
1068 var ns = new NativeString(nslen + 1)
1069 ns[nslen] = 0u8
1070 native_int_to_s(ns, nslen + 1)
1071 return new FlatString.full(ns, nslen, 0, nslen - 1, nslen)
1072 end
1073 end
1074
1075 redef class Array[E]
1076
1077 # Fast implementation
1078 redef fun plain_to_s
1079 do
1080 var l = length
1081 if l == 0 then return ""
1082 if l == 1 then if self[0] == null then return "" else return self[0].to_s
1083 var its = _items
1084 var na = new NativeArray[String](l)
1085 var i = 0
1086 var sl = 0
1087 var mypos = 0
1088 while i < l do
1089 var itsi = its[i]
1090 if itsi == null then
1091 i += 1
1092 continue
1093 end
1094 var tmp = itsi.to_s
1095 sl += tmp.bytelen
1096 na[mypos] = tmp
1097 i += 1
1098 mypos += 1
1099 end
1100 var ns = new NativeString(sl + 1)
1101 ns[sl] = 0u8
1102 i = 0
1103 var off = 0
1104 while i < mypos do
1105 var tmp = na[i]
1106 if tmp isa FlatString then
1107 var tpl = tmp._bytelen
1108 tmp._items.copy_to(ns, tpl, tmp._first_byte, off)
1109 off += tpl
1110 else
1111 for j in tmp.substrings do
1112 var s = j.as(FlatString)
1113 var slen = s._bytelen
1114 s._items.copy_to(ns, slen, s._first_byte, off)
1115 off += slen
1116 end
1117 end
1118 i += 1
1119 end
1120 return ns.to_s_with_length(sl)
1121 end
1122 end
1123
1124 redef class NativeArray[E]
1125 redef fun native_to_s do
1126 assert self isa NativeArray[String]
1127 var l = length
1128 var na = self
1129 var i = 0
1130 var sl = 0
1131 var mypos = 0
1132 while i < l do
1133 sl += na[i].bytelen
1134 i += 1
1135 mypos += 1
1136 end
1137 var ns = new NativeString(sl + 1)
1138 ns[sl] = 0u8
1139 i = 0
1140 var off = 0
1141 while i < mypos do
1142 var tmp = na[i]
1143 if tmp isa FlatString then
1144 var tpl = tmp._bytelen
1145 tmp._items.copy_to(ns, tpl, tmp._first_byte, off)
1146 off += tpl
1147 else
1148 for j in tmp.substrings do
1149 var s = j.as(FlatString)
1150 var slen = s._bytelen
1151 s._items.copy_to(ns, slen, s._first_byte, off)
1152 off += slen
1153 end
1154 end
1155 i += 1
1156 end
1157 return ns.to_s_with_length(sl)
1158 end
1159 end
1160
1161 redef class Map[K,V]
1162 redef fun join(sep, couple_sep)
1163 do
1164 if is_empty then return ""
1165
1166 var s = new Buffer # Result
1167
1168 # Concat first item
1169 var i = iterator
1170 var k = i.key
1171 var e = i.item
1172 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1173
1174 # Concat other _items
1175 i.next
1176 while i.is_ok do
1177 s.append(sep)
1178 k = i.key
1179 e = i.item
1180 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1181 i.next
1182 end
1183 return s.to_s
1184 end
1185 end