core/flat: Fix bug in `FlatBuffer::substring`
[nit.git] / lib / core / text / flat.nit
index 2d4a942..d58aafb 100644 (file)
@@ -36,18 +36,18 @@ end
 
 redef class FlatText
 
-       private fun first_byte: Int do return 0
+       fun first_byte: Int do return 0
 
-       private fun last_byte: Int do return _bytelen - 1
+       fun last_byte: Int do return _bytelen - 1
 
        # Cache of the latest position (char) explored in the string
-       private var position: Int = 0
+       var position: Int = 0
 
        # Cached position (bytes) in the NativeString underlying the String
-       private var bytepos: Int = 0
+       var bytepos: Int = 0
 
        # Index of the character `index` in `_items`
-       private fun char_to_byte_index(index: Int): Int do
+       fun char_to_byte_index(index: Int): Int do
                var ln = length
                assert index >= 0
                assert index < ln
@@ -85,12 +85,110 @@ redef class FlatText
                return ns_i
        end
 
+       # By escaping `self` to HTML, how many more bytes will be needed ?
+       fun chars_to_html_escape: Int do
+               var its = _items
+               var max = last_byte
+               var pos = first_byte
+               var endlen = 0
+               while pos <= max do
+                       var c = its[pos]
+                       if c == 0x3Cu8 then
+                               endlen += 3
+                       else if c == 0x3Eu8 then
+                               endlen += 3
+                       else if c == 0x26u8 then
+                               endlen += 4
+                       else if c == 0x22u8 then
+                               endlen += 4
+                       else if c == 0x27u8 then
+                               endlen += 4
+                       else if c == 0x2Fu8 then
+                               endlen += 4
+                       end
+                       pos += 1
+               end
+               return endlen
+       end
+
+       redef fun html_escape
+       do
+               var extra = chars_to_html_escape
+               if extra == 0 then return to_s
+               var its = _items
+               var max = last_byte
+               var pos = first_byte
+               var nlen = extra + _bytelen
+               var nits = new NativeString(nlen)
+               var outpos = 0
+               while pos <= max do
+                       var c = its[pos]
+                       # Special codes:
+                       # Some HTML characters are used as meta-data, they need
+                       # to be replaced by an HTML-Escaped equivalent
+                       #
+                       # * 0x3C (<) => &lt;
+                       # * 0x3E (>) => &gt;
+                       # * 0x26 (&) => &amp;
+                       # * 0x22 (") => &#34;
+                       # * 0x27 (') => &#39;
+                       # * 0x2F (/) => &#47;
+                       if c == 0x3Cu8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x6Cu8
+                               nits[outpos + 2] = 0x74u8
+                               nits[outpos + 3] = 0x3Bu8
+                               outpos += 4
+                       else if c == 0x3Eu8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x67u8
+                               nits[outpos + 2] = 0x74u8
+                               nits[outpos + 3] = 0x3Bu8
+                               outpos += 4
+                       else if c == 0x26u8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x61u8
+                               nits[outpos + 2] = 0x6Du8
+                               nits[outpos + 3] = 0x70u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else if c == 0x22u8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x23u8
+                               nits[outpos + 2] = 0x33u8
+                               nits[outpos + 3] = 0x34u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else if c == 0x27u8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x23u8
+                               nits[outpos + 2] = 0x33u8
+                               nits[outpos + 3] = 0x39u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else if c == 0x2Fu8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x23u8
+                               nits[outpos + 2] = 0x34u8
+                               nits[outpos + 3] = 0x37u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else
+                               nits[outpos] = c
+                               outpos += 1
+                       end
+                       pos += 1
+               end
+               var s = new FlatString.with_infos(nits, nlen, 0, nlen - 1)
+               return s
+       end
+
        # By escaping `self` to C, how many more bytes will be needed ?
        #
        # This enables a double-optimization in `escape_to_c` since if this
        # method returns 0, then `self` does not need escaping and can be
        # returned as-is
-       protected fun chars_to_escape_to_c: Int do
+       fun chars_to_escape_to_c: Int do
                var its = _items
                var max = last_byte
                var pos = first_byte
@@ -200,6 +298,14 @@ class FlatString
                return _items.utf8_length(_first_byte, _last_byte)
        end
 
+       redef var to_cstring is lazy do
+               var blen = _bytelen
+               var new_items = new NativeString(blen + 1)
+               _items.copy_to(new_items, blen, _first_byte, 0)
+               new_items[blen] = 0u8
+               return new_items
+       end
+
        redef fun reversed
        do
                var b = new FlatBuffer.with_capacity(_bytelen + 1)
@@ -304,16 +410,6 @@ class FlatString
                _bytepos = from
        end
 
-       redef fun to_cstring do
-               if real_items != null then return real_items.as(not null)
-               var blen = _bytelen
-               var new_items = new NativeString(blen + 1)
-               _items.copy_to(new_items, blen, _first_byte, 0)
-               new_items[blen] = 0u8
-               real_items = new_items
-               return new_items
-       end
-
        redef fun ==(other)
        do
                if not other isa FlatString then return super
@@ -582,6 +678,9 @@ class FlatBuffer
 
        private var capacity = 0
 
+       # Real items, used as cache for when to_cstring is called
+       private var real_items: NativeString is noinit
+
        redef fun fast_cstring do return _items.fast_cstring(0)
 
        redef fun substrings do return new FlatSubstringsIter(self)
@@ -702,7 +801,7 @@ class FlatBuffer
                        real_items = new_native
                        is_dirty = false
                end
-               return real_items.as(not null)
+               return real_items
        end
 
        # Create a new empty string.
@@ -779,18 +878,15 @@ class FlatBuffer
                assert count >= 0
                if from < 0 then from = 0
                if (from + count) > length then count = length - from
-               if count != 0 then
-                       var its = _items
-                       var bytefrom = its.char_to_byte_index(from)
-                       var byteto = its.char_to_byte_index(count + from - 1)
-                       byteto += its.char_at(byteto).u8char_len - 1
-                       var byte_length = byteto - bytefrom + 1
-                       var r_items = new NativeString(byte_length)
-                       its.copy_to(r_items, byte_length, bytefrom, 0)
-                       return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
-               else
-                       return new Buffer
-               end
+               if count <= 0 then return new Buffer
+               var its = _items
+               var bytefrom = its.char_to_byte_index(from)
+               var byteto = its.char_to_byte_index(count + from - 1)
+               byteto += its.char_at(byteto).u8char_len - 1
+               var byte_length = byteto - bytefrom + 1
+               var r_items = new NativeString(byte_length)
+               its.copy_to(r_items, byte_length, bytefrom, 0)
+               return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
        end
 
        redef fun reverse
@@ -1002,7 +1098,7 @@ redef class NativeString
                copy_to(new_self, length, 0, 0)
                var str = new FlatString.with_infos(new_self, length, 0, length - 1)
                new_self[length] = 0u8
-               str.real_items = new_self
+               str.to_cstring = new_self
                return str
        end
 
@@ -1035,7 +1131,7 @@ redef class NativeString
                        end
                        var ok_c: Bool
                        var c = char_at(pos)
-                       var cp = c.ascii
+                       var cp = c.code_point
                        if nxst == 1 then
                                ok_c = cp >= 0 and cp <= 0x7F
                        else if nxst == 2 then
@@ -1147,8 +1243,9 @@ redef class Array[E]
        do
                var l = length
                if l == 0 then return ""
-               if l == 1 then if self[0] == null then return "" else return self[0].to_s
-               var its = _items
+               var its = _items.as(not null)
+               var first = its[0]
+               if l == 1 then if first == null then return "" else return first.to_s
                var na = new NativeArray[String](l)
                var i = 0
                var sl = 0
@@ -1185,7 +1282,7 @@ redef class Array[E]
                        end
                        i += 1
                end
-               return ns.to_s_with_length(sl)
+               return new FlatString.with_infos(ns, sl, 0, sl - 1)
        end
 end
 
@@ -1222,7 +1319,7 @@ redef class NativeArray[E]
                        end
                        i += 1
                end
-               return ns.to_s_with_length(sl)
+               return new FlatString.with_infos(ns, sl, 0, sl - 1)
        end
 end