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