+ redef fun length do return target.length
+
+ redef fun iterator: IndexedIterator[Char] do return self.iterator_from(0)
+
+ redef fun reverse_iterator do return self.reverse_iterator_from(self.length - 1)
+end
+
+# View on Buffer objects, extends Sequence
+# for mutation operations
+private abstract class BufferCharView
+ super StringCharView
+ super Sequence[Char]
+
+ redef type SELFTYPE: Buffer
+
+end
+
+# A `String` holds and manipulates an arbitrary sequence of characters.
+#
+# String objects may be created using literals.
+#
+# assert "Hello World!" isa String
+abstract class String
+ super Text
+
+ redef type SELFTYPE: String is fixed
+
+ redef fun to_s do return self
+
+ # Concatenates `o` to `self`
+ #
+ # assert "hello" + "world" == "helloworld"
+ # assert "" + "hello" + "" == "hello"
+ fun +(o: Text): SELFTYPE is abstract
+
+ # Concatenates self `i` times
+ #
+ # assert "abc" * 4 == "abcabcabcabc"
+ # assert "abc" * 1 == "abc"
+ # assert "abc" * 0 == ""
+ fun *(i: Int): SELFTYPE is abstract
+
+ # Insert `s` at `pos`.
+ #
+ # assert "helloworld".insert_at(" ", 5) == "hello world"
+ fun insert_at(s: String, pos: Int): SELFTYPE is abstract
+
+ redef fun substrings is abstract
+
+ # Returns a reversed version of self
+ #
+ # assert "hello".reversed == "olleh"
+ # assert "bob".reversed == "bob"
+ # assert "".reversed == ""
+ fun reversed: SELFTYPE is abstract
+
+ # A upper case version of `self`
+ #
+ # assert "Hello World!".to_upper == "HELLO WORLD!"
+ fun to_upper: SELFTYPE is abstract
+
+ # A lower case version of `self`
+ #
+ # assert "Hello World!".to_lower == "hello world!"
+ fun to_lower : SELFTYPE is abstract
+
+ # Takes a camel case `self` and converts it to snake case
+ #
+ # assert "randomMethodId".to_snake_case == "random_method_id"
+ #
+ # The rules are the following:
+ #
+ # An uppercase is always converted to a lowercase
+ #
+ # assert "HELLO_WORLD".to_snake_case == "hello_world"
+ #
+ # An uppercase that follows a lowercase is prefixed with an underscore
+ #
+ # assert "HelloTheWORLD".to_snake_case == "hello_the_world"
+ #
+ # An uppercase that follows an uppercase and is followed by a lowercase, is prefixed with an underscore
+ #
+ # assert "HelloTHEWorld".to_snake_case == "hello_the_world"
+ #
+ # All other characters are kept as is; `self` does not need to be a proper CamelCased string.
+ #
+ # assert "=-_H3ll0Th3W0rld_-=".to_snake_case == "=-_h3ll0th3w0rld_-="
+ fun to_snake_case: SELFTYPE
+ do
+ if self.is_lower then return self
+
+ var new_str = new FlatBuffer.with_capacity(self.length)
+ var prev_is_lower = false
+ var prev_is_upper = false
+
+ for i in [0..length[ do
+ var char = chars[i]
+ if char.is_lower then
+ new_str.add(char)
+ prev_is_lower = true
+ prev_is_upper = false
+ else if char.is_upper then
+ if prev_is_lower then
+ new_str.add('_')
+ else if prev_is_upper and i+1 < length and chars[i+1].is_lower then
+ new_str.add('_')
+ end
+ new_str.add(char.to_lower)
+ prev_is_lower = false
+ prev_is_upper = true
+ else
+ new_str.add(char)
+ prev_is_lower = false
+ prev_is_upper = false
+ end
+ end
+
+ return new_str.to_s
+ end
+
+ # Takes a snake case `self` and converts it to camel case
+ #
+ # assert "random_method_id".to_camel_case == "randomMethodId"
+ #
+ # If the identifier is prefixed by an underscore, the underscore is ignored
+ #
+ # assert "_private_field".to_camel_case == "_privateField"
+ #
+ # If `self` is upper, it is returned unchanged
+ #
+ # assert "RANDOM_ID".to_camel_case == "RANDOM_ID"
+ #
+ # If there are several consecutive underscores, they are considered as a single one
+ #
+ # assert "random__method_id".to_camel_case == "randomMethodId"
+ fun to_camel_case: SELFTYPE
+ do
+ if self.is_upper then return self
+
+ var new_str = new FlatBuffer
+ var is_first_char = true
+ var follows_us = false
+
+ for i in [0..length[ do
+ var char = chars[i]
+ if is_first_char then
+ new_str.add(char)
+ is_first_char = false
+ else if char == '_' then
+ follows_us = true
+ else if follows_us then
+ new_str.add(char.to_upper)
+ follows_us = false
+ else
+ new_str.add(char)
+ end
+ end
+
+ return new_str.to_s
+ end
+
+ # Returns a capitalized `self`
+ #
+ # Letters that follow a letter are lowercased
+ # Letters that follow a non-letter are upcased.
+ #
+ # SEE : `Char::is_letter` for the definition of letter.
+ #
+ # assert "jAVASCRIPT".capitalized == "Javascript"
+ # assert "i am root".capitalized == "I Am Root"
+ # assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
+ fun capitalized: SELFTYPE do
+ if length == 0 then return self
+
+ var buf = new FlatBuffer.with_capacity(length)
+
+ var curr = chars[0].to_upper
+ var prev = curr
+ buf[0] = curr
+
+ for i in [1 .. length[ do
+ prev = curr
+ curr = self[i]
+ if prev.is_letter then
+ buf[i] = curr.to_lower
+ else
+ buf[i] = curr.to_upper
+ end
+ end
+
+ return buf.to_s
+ end
+end
+
+private class FlatSubstringsIter
+ super Iterator[FlatText]
+
+ var tgt: nullable FlatText
+
+ redef fun item do
+ assert is_ok
+ return tgt.as(not null)
+ end
+
+ redef fun is_ok do return tgt != null
+
+ redef fun next do tgt = null
+end
+
+# Immutable strings of characters.
+class FlatString
+ super FlatText
+ super String
+
+ # Index in _items of the start of the string
+ private var index_from: Int is noinit
+
+ # Indes in _items of the last item of the string
+ private var index_to: Int is noinit
+
+ redef var chars = new FlatStringCharView(self) is lazy
+
+ redef fun [](index)
+ do
+ # Check that the index (+ index_from) is not larger than indexTo
+ # In other terms, if the index is valid
+ assert index >= 0
+ assert (index + index_from) <= index_to
+ return items[index + index_from]
+ end
+
+ ################################################
+ # AbstractString specific methods #
+ ################################################
+
+ redef fun reversed
+ do
+ var native = new NativeString(self.length + 1)
+ var length = self.length
+ var items = self.items
+ var pos = 0
+ var ipos = length-1
+ while pos < length do
+ native[pos] = items[ipos]
+ pos += 1
+ ipos -= 1
+ end
+ return native.to_s_with_length(self.length)
+ end
+
+ redef fun fast_cstring do return items.fast_cstring(index_from)
+
+ redef fun substring(from, count)
+ do
+ assert count >= 0
+
+ if from < 0 then
+ count += from
+ if count < 0 then count = 0
+ from = 0
+ end
+
+ var new_from = index_from + from
+
+ if (new_from + count) > index_to then
+ var new_len = index_to - new_from + 1
+ if new_len <= 0 then return empty
+ return new FlatString.with_infos(items, new_len, new_from, index_to)
+ end
+
+ if count <= 0 then return empty
+
+ var to = new_from + count - 1
+
+ return new FlatString.with_infos(items, to - new_from + 1, new_from, to)
+ end
+
+ redef fun empty do return "".as(FlatString)
+
+ redef fun to_upper
+ do
+ var outstr = new NativeString(self.length + 1)
+ var out_index = 0
+
+ var myitems = self.items
+ var index_from = self.index_from
+ var max = self.index_to
+
+ while index_from <= max do
+ outstr[out_index] = myitems[index_from].to_upper
+ out_index += 1
+ index_from += 1
+ end
+
+ outstr[self.length] = '\0'
+
+ return outstr.to_s_with_length(self.length)
+ end
+
+ redef fun to_lower
+ do
+ var outstr = new NativeString(self.length + 1)
+ var out_index = 0
+
+ var myitems = self.items
+ var index_from = self.index_from
+ var max = self.index_to
+
+ while index_from <= max do
+ outstr[out_index] = myitems[index_from].to_lower
+ out_index += 1
+ index_from += 1
+ end
+
+ outstr[self.length] = '\0'
+
+ return outstr.to_s_with_length(self.length)
+ end
+
+ redef fun output
+ do
+ var i = self.index_from
+ var imax = self.index_to
+ while i <= imax do
+ items[i].output
+ i += 1
+ end
+ end
+
+ ##################################################
+ # String Specific Methods #
+ ##################################################
+
+ # Low-level creation of a new string with given data.
+ #
+ # `items` will be used as is, without copy, to retrieve the characters of the string.
+ # Aliasing issues is the responsibility of the caller.
+ private init with_infos(items: NativeString, length: Int, from: Int, to: Int)
+ do
+ self.items = items
+ self.length = length
+ index_from = from
+ index_to = to
+ end
+
+ redef fun to_cstring do
+ if real_items != null then
+ return real_items.as(not null)
+ else
+ var newItems = new NativeString(length + 1)
+ self.items.copy_to(newItems, length, index_from, 0)
+ newItems[length] = '\0'
+ self.real_items = newItems
+ return newItems
+ end
+ end
+
+ redef fun ==(other)
+ do
+ if not other isa FlatString then return super
+
+ if self.object_id == other.object_id then return true
+
+ var my_length = length
+
+ if other.length != my_length then return false
+
+ var my_index = index_from
+ var its_index = other.index_from
+
+ var last_iteration = my_index + my_length
+
+ var itsitems = other.items
+ var myitems = self.items
+
+ while my_index < last_iteration do
+ if myitems[my_index] != itsitems[its_index] then return false
+ my_index += 1
+ its_index += 1
+ end
+
+ return true
+ end
+
+ redef fun <(other)
+ do
+ if not other isa FlatString then return super
+
+ if self.object_id == other.object_id then return false
+
+ var my_curr_char : Char
+ var its_curr_char : Char
+
+ var curr_id_self = self.index_from
+ var curr_id_other = other.index_from
+
+ var my_items = self.items
+ var its_items = other.items
+
+ var my_length = self.length
+ var its_length = other.length
+
+ var max_iterations = curr_id_self + my_length
+
+ while curr_id_self < max_iterations do
+ my_curr_char = my_items[curr_id_self]
+ its_curr_char = its_items[curr_id_other]
+
+ if my_curr_char != its_curr_char then
+ if my_curr_char < its_curr_char then return true
+ return false
+ end
+
+ curr_id_self += 1
+ curr_id_other += 1
+ end
+
+ return my_length < its_length
+ end
+
+ redef fun +(s)
+ do
+ var my_length = self.length
+ var its_length = s.length
+
+ var total_length = my_length + its_length
+
+ var target_string = new NativeString(my_length + its_length + 1)
+
+ self.items.copy_to(target_string, my_length, index_from, 0)
+ if s isa FlatString then
+ s.items.copy_to(target_string, its_length, s.index_from, my_length)
+ else if s isa FlatBuffer then
+ s.items.copy_to(target_string, its_length, 0, my_length)
+ else
+ var curr_pos = my_length
+ for i in [0..s.length[ do
+ var c = s.chars[i]
+ target_string[curr_pos] = c
+ curr_pos += 1
+ end
+ end
+
+ target_string[total_length] = '\0'
+
+ return target_string.to_s_with_length(total_length)
+ end
+
+ redef fun *(i)
+ do
+ assert i >= 0
+
+ var my_length = self.length
+
+ var final_length = my_length * i
+
+ var my_items = self.items
+
+ var target_string = new NativeString(final_length + 1)
+
+ target_string[final_length] = '\0'
+
+ var current_last = 0
+
+ for iteration in [1 .. i] do
+ my_items.copy_to(target_string, my_length, 0, current_last)
+ current_last += my_length
+ end
+
+ return target_string.to_s_with_length(final_length)
+ end
+
+ redef fun hash
+ do
+ if hash_cache == null then
+ # djb2 hash algorithm
+ var h = 5381
+ var i = index_from
+
+ var myitems = items
+
+ while i <= index_to do
+ h = h.lshift(5) + h + myitems[i].ascii
+ i += 1
+ end
+
+ hash_cache = h
+ end
+
+ return hash_cache.as(not null)
+ end
+
+ redef fun substrings do return new FlatSubstringsIter(self)
+end
+
+private class FlatStringReverseIterator
+ super IndexedIterator[Char]
+
+ var target: FlatString
+
+ var target_items: NativeString
+
+ var curr_pos: Int
+
+ init with_pos(tgt: FlatString, pos: Int)
+ do
+ target = tgt
+ target_items = tgt.items
+ curr_pos = pos + tgt.index_from
+ end
+
+ redef fun is_ok do return curr_pos >= target.index_from
+
+ redef fun item do return target_items[curr_pos]
+
+ redef fun next do curr_pos -= 1
+
+ redef fun index do return curr_pos - target.index_from
+
+end
+
+private class FlatStringIterator
+ super IndexedIterator[Char]
+
+ var target: FlatString
+
+ var target_items: NativeString
+
+ var curr_pos: Int
+
+ init with_pos(tgt: FlatString, pos: Int)
+ do
+ target = tgt
+ target_items = tgt.items
+ curr_pos = pos + target.index_from
+ end
+
+ redef fun is_ok do return curr_pos <= target.index_to
+
+ redef fun item do return target_items[curr_pos]
+
+ redef fun next do curr_pos += 1
+
+ redef fun index do return curr_pos - target.index_from
+
+end
+
+private class FlatStringCharView
+ super StringCharView
+
+ redef type SELFTYPE: FlatString
+
+ redef fun [](index)
+ do
+ # Check that the index (+ index_from) is not larger than indexTo
+ # In other terms, if the index is valid
+ assert index >= 0
+ var target = self.target
+ assert (index + target.index_from) <= target.index_to
+ return target.items[index + target.index_from]
+ end
+
+ redef fun iterator_from(start) do return new FlatStringIterator.with_pos(target, start)
+
+ redef fun reverse_iterator_from(start) do return new FlatStringReverseIterator.with_pos(target, start)
+
+end
+
+# A mutable sequence of characters.
+abstract class Buffer
+ super Text
+
+ redef type SELFTYPE: Buffer is fixed
+
+ # Specific implementations MUST set this to `true` in order to invalidate caches
+ protected var is_dirty = true
+
+ # Copy-On-Write flag
+ #
+ # If the `Buffer` was to_s'd, the next in-place altering
+ # operation will cause the current `Buffer` to be re-allocated.
+ #
+ # The flag will then be set at `false`.
+ protected var written = false
+
+ # Modifies the char contained at pos `index`
+ #
+ # DEPRECATED : Use self.chars.[]= instead
+ fun []=(index: Int, item: Char) is abstract
+
+ # Adds a char `c` at the end of self
+ #
+ # DEPRECATED : Use self.chars.add instead
+ fun add(c: Char) is abstract
+
+ # Clears the buffer
+ #
+ # var b = new FlatBuffer
+ # b.append "hello"
+ # assert not b.is_empty
+ # b.clear
+ # assert b.is_empty
+ fun clear is abstract
+
+ # Enlarges the subsequent array containing the chars of self
+ fun enlarge(cap: Int) is abstract
+
+ # Adds the content of text `s` at the end of self
+ #
+ # var b = new FlatBuffer
+ # b.append "hello"
+ # b.append "world"
+ # assert b == "helloworld"
+ fun append(s: Text) is abstract
+
+ # `self` is appended in such a way that `self` is repeated `r` times
+ #
+ # var b = new FlatBuffer
+ # b.append "hello"
+ # b.times 3
+ # assert b == "hellohellohello"
+ fun times(r: Int) is abstract
+
+ # Reverses itself in-place
+ #
+ # var b = new FlatBuffer
+ # b.append("hello")
+ # b.reverse
+ # assert b == "olleh"
+ fun reverse is abstract
+
+ # Changes each lower-case char in `self` by its upper-case variant
+ #
+ # var b = new FlatBuffer
+ # b.append("Hello World!")
+ # b.upper
+ # assert b == "HELLO WORLD!"
+ fun upper is abstract
+
+ # Changes each upper-case char in `self` by its lower-case variant
+ #
+ # var b = new FlatBuffer
+ # b.append("Hello World!")
+ # b.lower
+ # assert b == "hello world!"
+ fun lower is abstract
+
+ # Capitalizes each word in `self`
+ #
+ # Letters that follow a letter are lowercased
+ # Letters that follow a non-letter are upcased.
+ #
+ # SEE: `Char::is_letter` for the definition of a letter.
+ #
+ # var b = new FlatBuffer.from("jAVAsCriPt")
+ # b.capitalize
+ # assert b == "Javascript"
+ # b = new FlatBuffer.from("i am root")
+ # b.capitalize
+ # assert b == "I Am Root"
+ # b = new FlatBuffer.from("ab_c -ab0c ab\nc")
+ # b.capitalize
+ # assert b == "Ab_C -Ab0C Ab\nC"
+ fun capitalize do
+ if length == 0 then return
+ var c = self[0].to_upper
+ self[0] = c
+ var prev = c
+ for i in [1 .. length[ do
+ prev = c
+ c = self[i]
+ if prev.is_letter then
+ self[i] = c.to_lower
+ else
+ self[i] = c.to_upper
+ end
+ end
+ end
+
+ redef fun hash
+ do
+ if is_dirty then hash_cache = null
+ return super
+ end
+
+ # In Buffers, the internal sequence of character is mutable
+ # Thus, `chars` can be used to modify the buffer.
+ redef fun chars: Sequence[Char] is abstract
+end
+
+# Mutable strings of characters.
+class FlatBuffer
+ super FlatText
+ super Buffer
+
+ redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
+
+ private var capacity: Int = 0
+
+ redef fun fast_cstring do return items.fast_cstring(0)
+
+ redef fun substrings do return new FlatSubstringsIter(self)
+
+ # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
+ #
+ # This happens when an operation modifies the current `Buffer` and
+ # the Copy-On-Write flag `written` is set at true.
+ private fun reset do
+ var nns = new NativeString(capacity)
+ items.copy_to(nns, length, 0, 0)
+ items = nns
+ written = false
+ end
+
+ redef fun [](index)
+ do
+ assert index >= 0
+ assert index < length
+ return items[index]
+ end
+
+ redef fun []=(index, item)
+ do
+ is_dirty = true
+ if index == length then
+ add(item)
+ return
+ end
+ if written then reset
+ assert index >= 0 and index < length
+ items[index] = item
+ end
+
+ redef fun add(c)
+ do
+ is_dirty = true
+ if capacity <= length then enlarge(length + 5)
+ items[length] = c
+ length += 1
+ end
+
+ redef fun clear do
+ is_dirty = true
+ if written then reset
+ length = 0
+ end
+
+ redef fun empty do return new FlatBuffer
+
+ redef fun enlarge(cap)
+ do
+ var c = capacity
+ if cap <= c then return
+ while c <= cap do c = c * 2 + 2
+ # The COW flag can be set at false here, since
+ # it does a copy of the current `Buffer`
+ written = false
+ var a = new NativeString(c+1)
+ if length > 0 then items.copy_to(a, length, 0, 0)
+ items = a
+ capacity = c