@internal class String { fun equals(rhs: String) -> Bool { var i = 0; if self.length() != rhs.length() { return false; } let len = self.length(); while i < len { if self.getByte(i) != rhs.getByte(i) { return false; } i = i + 1; } return true; } @internal fun compareTo(rhs: String) -> Int; @internal fun length() -> Int; @internal fun parseInt() -> Int; @internal fun parseLong() -> Long; @internal fun plus(rhs: String) -> String; @internal fun getByte(idx: Int) -> Byte; @internal fun clone() -> String; @internal @static fun fromBytesPartOrNull(val: Array[Byte], offset: Int, len: Int) -> String; @internal @static fun fromStringPartOrNull(val: String, offset: Int, len: Int) -> String; @static fun fromBytesPart(val: Array[Byte], offset: Int, len: Int) throws -> String { let str = String::fromBytesPartOrNull(val, offset, len); if str === nil { throw "invalid utf-8 encoding."; } return str; } @static fun fromBytes(val: Array[Byte]) throws -> String { let str = String::fromBytesPartOrNull(val, 0, val.length()); if str === nil { throw "invalid utf-8 encoding."; } return str; } @static fun fromStringPart(val: String, offset: Int, len: Int) throws -> String { let str = String::fromStringPartOrNull(val, offset, len); if str === nil { throw "invalid utf-8 encoding."; } return str; } @static fun fromString(val: String) -> String = val.clone(); fun isEmpty() -> Bool = self.length() == 0; fun codePoints() -> StringCodePointIterator = StringCodePointIterator(self, 0); } class StringCodePointIterator(let value: String, var ind: Int) { fun hasNext() -> Bool = self.ind < self.value.length(); fun next() -> Char { var ch = self.value.getByte(self.ind); if ch <= 0x7FY { self.ind = self.ind + 1; return ch.toInt().toCharUnchecked(); } if ch <= 0xBFY { fatalError("unexpected continuation byte"); } if ch <= 0xDFY { let cp = (ch.toInt() & 0x1F) << 6 | (self.value.getByte(self.ind + 1).toInt() & 0x3F); self.ind = self.ind + 2; return cp.toCharUnchecked(); } if ch <= 0xEFY { let cp = (ch.toInt() & 0xF) << 12 | (self.value.getByte(self.ind + 1).toInt() & 0x3F) << 6 | (self.value.getByte(self.ind + 2).toInt() & 0x3F); self.ind = self.ind + 3; return cp.toCharUnchecked(); } if ch <= 0xF7Y { let cp = (ch.toInt() & 0x3) << 18 | (self.value.getByte(self.ind + 1).toInt() & 0x3F) << 12 | (self.value.getByte(self.ind + 2).toInt() & 0x3F) << 6 | (self.value.getByte(self.ind + 3).toInt() & 0x3F); self.ind = self.ind + 4; return cp.toCharUnchecked(); } fatalError("invalid utf8: " + ch.toInt().toString()); return '\0'; } fun length() -> Int { var i = 0; while self.hasNext() { i = i + 1; self.next(); } return i; } fun toString() -> String = try! String::fromStringPart(self.value, self.ind, self.value.length() - self.ind); }