瀏覽代碼

AK: Add substring methods to Utf8View

This patch implements a Unicode-safe substring method, which can be used
when offset and length should be specified in actual characters instead
of bytes.

This can be used to mitigate issues where a string is split in the
middle of a UTF-8 multi-byte character, which leads to invalid UTF-8.

Furthermore, it implements to common shorthands for substring methods
which take only an offset and return the substring until the end of the
string.
Max Wipfli 4 年之前
父節點
當前提交
c1b452f754
共有 2 個文件被更改,包括 26 次插入1 次删除
  1. 20 0
      AK/Utf8View.cpp
  2. 6 1
      AK/Utf8View.h

+ 20 - 0
AK/Utf8View.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
+ * Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -59,6 +60,25 @@ Utf8View Utf8View::substring_view(size_t byte_offset, size_t byte_length) const
     return Utf8View { string };
     return Utf8View { string };
 }
 }
 
 
+Utf8View Utf8View::unicode_substring_view(size_t codepoint_offset, size_t codepoint_length) const
+{
+    if (codepoint_length == 0)
+        return {};
+
+    size_t codepoint_index = 0, offset_in_bytes = 0;
+    for (auto iterator = begin(); !iterator.done(); ++iterator) {
+        if (codepoint_index == codepoint_offset)
+            offset_in_bytes = byte_offset_of(iterator);
+        if (codepoint_index == codepoint_offset + codepoint_length - 1) {
+            size_t length_in_bytes = byte_offset_of(++iterator) - offset_in_bytes;
+            return substring_view(offset_in_bytes, length_in_bytes);
+        }
+        ++codepoint_index;
+    }
+
+    VERIFY_NOT_REACHED();
+}
+
 static inline bool decode_first_byte(
 static inline bool decode_first_byte(
     unsigned char byte,
     unsigned char byte,
     size_t& out_code_point_length_in_bytes,
     size_t& out_code_point_length_in_bytes,

+ 6 - 1
AK/Utf8View.h

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
+ * Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -57,9 +58,13 @@ public:
     const unsigned char* bytes() const { return begin_ptr(); }
     const unsigned char* bytes() const { return begin_ptr(); }
     size_t byte_length() const { return m_string.length(); }
     size_t byte_length() const { return m_string.length(); }
     size_t byte_offset_of(const Utf8CodepointIterator&) const;
     size_t byte_offset_of(const Utf8CodepointIterator&) const;
+
     Utf8View substring_view(size_t byte_offset, size_t byte_length) const;
     Utf8View substring_view(size_t byte_offset, size_t byte_length) const;
-    bool is_empty() const { return m_string.is_empty(); }
+    Utf8View substring_view(size_t byte_offset) const { return substring_view(byte_offset, byte_length() - byte_offset); }
+    Utf8View unicode_substring_view(size_t codepoint_offset, size_t codepoint_length) const;
+    Utf8View unicode_substring_view(size_t codepoint_offset) const { return unicode_substring_view(codepoint_offset, length() - codepoint_offset); }
 
 
+    bool is_empty() const { return m_string.is_empty(); }
     bool starts_with(const Utf8View&) const;
     bool starts_with(const Utf8View&) const;
 
 
     size_t iterator_offset(const Utf8CodepointIterator& it) const
     size_t iterator_offset(const Utf8CodepointIterator& it) const