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