Skip to content

Commit

Permalink
add rev_get for StringView
Browse files Browse the repository at this point in the history
  • Loading branch information
Yu-zh authored and bobzhang committed Jan 14, 2025
1 parent 5c7d43c commit 639ffb2
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 1 deletion.
1 change: 1 addition & 0 deletions string/string.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ impl StringView {
length_ge(Self, Int) -> Bool
op_as_view(Self, start~ : Int = .., end? : Int) -> Self
op_get(Self, Int) -> Char
rev_get(Self, Int) -> Char
}
impl Show for StringView

Expand Down
56 changes: 55 additions & 1 deletion string/view.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@ pub fn op_as_view(
///|
/// Return the character at the given index.
///
/// This method has O(n) complexity.
/// The time complexity is O(n) where n is the given index, as it needs to scan
/// through the UTF-16 code units to find the nth Unicode character.
///
/// # Example
///
Expand Down Expand Up @@ -352,6 +353,59 @@ pub fn op_get(self : StringView, index : Int) -> Char {
}
}

///|
/// Returns the character at the given index from the end of the view.
///
/// The time complexity is O(n) where n is the given index, as it needs to scan
/// through the UTF-16 code units to find the nth Unicode character.
///
/// # Example
///
/// ```
/// let str = "Hello🤣🤣🤣"
/// guard let Some(start) = str.index_at(1)
/// guard let Some(end) = str.index_at(6)
/// let view = str[start:end]
/// inspect!(view.rev_get(0), content="'🤣'")
/// inspect!(view.rev_get(4), content="'e'")
/// ```
pub fn rev_get(self : StringView, index : Int) -> Char {
guard index >= 0 else {
abort("Index out of bounds: cannot access negative index")
}
let mut utf16_offset = self.end - 1
let mut char_count = 0
while char_count < index && utf16_offset >= self.start {
let c1 = self.str[utf16_offset]
if is_trailing_surrogate(c1) && utf16_offset - 1 >= self.start {
let c2 = self.str[utf16_offset - 1]
if is_leading_surrogate(c2) {
utf16_offset = utf16_offset - 2
char_count = char_count + 1
continue
} else {
abort("invalid surrogate pair")
}
}
utf16_offset = utf16_offset - 1
char_count = char_count + 1
}
guard char_count == index && utf16_offset >= self.start else {
abort("Index out of bounds: cannot access index \{index} in reverse")
}
let c1 = self.str[utf16_offset]
if is_trailing_surrogate(c1) {
let c2 = self.str[utf16_offset - 1]
if is_leading_surrogate(c2) {
code_point_of_surrogate_pair(c2, c1)
} else {
abort("invalid surrogate pair")
}
} else {
c1
}
}

///|
pub impl Show for StringView with output(self, logger) {
let substr = self.str.substring(start=self.start, end=self.end)
Expand Down
27 changes: 27 additions & 0 deletions string/view_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ test "stringview op_get" {
inspect!(view[4], content="'🤣'")
}

test "stringview rev_get" {
let str = "Hello🤣🤣🤣"
guard let Some(start) = str.index_at(1)
guard let Some(end) = str.index_at(6)
let view = str[start:end]
inspect!(view.rev_get(0), content="'🤣'")
inspect!(view.rev_get(4), content="'e'")
}

test "stringview length" {
let str = "Hello🤣🤣🤣"
guard let Some(start) = str.index_at(1)
Expand Down Expand Up @@ -417,3 +426,21 @@ test "panic negative index 9" {
let _ = str_view[2:-2]

}

test "panic rev_get" {
let str = "Hello🤣🤣🤣"
guard let Some(start) = str.index_at(1)
guard let Some(end) = str.index_at(6)
let view = str[start:end]
let _ = view.rev_get(5)

}

test "panic rev_get2" {
let str = "Hello🤣🤣🤣"
guard let Some(start) = str.index_at(1)
guard let Some(end) = str.index_at(6)
let view = str[start:end]
let _ = view.rev_get(-1)

}

0 comments on commit 639ffb2

Please sign in to comment.