Add a fuzzer for UnicodeStringAppendable.

Bug:1009107
Change-Id: I02f55ede8e1afc713141ab95046ebb7a732f12a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/deps/icu/+/1853110
Reviewed-by: Max Moroz <mmoroz@chromium.org>
diff --git a/fuzzers/BUILD.gn b/fuzzers/BUILD.gn
index 3053d79..07a40c0 100644
--- a/fuzzers/BUILD.gn
+++ b/fuzzers/BUILD.gn
@@ -4,8 +4,8 @@
 
 # ICU fuzzers.
 
-import("//third_party/icu/config.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
+import("//third_party/icu/config.gni")
 
 # root BUILD depenends on this target. Needed for package discovery
 group("fuzzers") {
@@ -93,3 +93,12 @@
   ]
   libfuzzer_options = [ "max_len=10240" ]
 }
+
+fuzzer_test("icu_appendable_fuzzer") {
+  sources = [
+    "icu_appendable_fuzzer.cc",
+  ]
+  deps = [
+    ":fuzzer_support",
+  ]
+}
diff --git a/fuzzers/fuzzer_utils.h b/fuzzers/fuzzer_utils.h
index df69966..3572156 100644
--- a/fuzzers/fuzzer_utils.h
+++ b/fuzzers/fuzzer_utils.h
@@ -51,4 +51,13 @@
   return icu::UnicodeString::fromUTF32(uchars.data(), uchars.size());
 }
 
+std::vector<char16_t> RandomChar16Array(size_t random_value,
+                                        const uint8_t* data,
+                                        size_t size) {
+  std::vector<char16_t> arr;
+  arr.resize(random_value % size * sizeof(uint8_t) / sizeof(char16_t));
+  memcpy(arr.data(), data, arr.size() * sizeof(char16_t) / sizeof(uint8_t));
+  return arr;
+}
+
 #endif  // THIRD_PARTY_ICU_FUZZERS_FUZZER_UTILS_H_
diff --git a/fuzzers/icu_appendable_fuzzer.cc b/fuzzers/icu_appendable_fuzzer.cc
new file mode 100644
index 0000000..6274242
--- /dev/null
+++ b/fuzzers/icu_appendable_fuzzer.cc
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <vector>
+#include "third_party/icu/fuzzers/fuzzer_utils.h"
+#include "third_party/icu/source/common/unicode/appendable.h"
+#include "third_party/libFuzzer/src/utils/FuzzedDataProvider.h"
+
+static IcuEnvironment* env = new IcuEnvironment;
+
+constexpr size_t kMaxInitialSize = 64;
+constexpr size_t kMaxReserveSize = 4096;
+constexpr size_t kMaxAppendLength = 64;
+constexpr size_t kMaxAdditionalDesiredSize = 4096;
+
+constexpr size_t kScratchBufSize = 4096;
+char16_t scratch_buf[kScratchBufSize];
+
+enum class AppendableApi {
+  AppendCodeUnit,
+  AppendCodePoint,
+  AppendString,
+  ReserveAppendCapacity,
+  GetAppendBuffer,
+  kMaxValue = GetAppendBuffer
+};
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider provider(data, size);
+  auto str(icu::UnicodeString::fromUTF8(
+      provider.ConsumeRandomLengthString(kMaxInitialSize)));
+  icu::UnicodeStringAppendable strAppendable(str);
+
+  while (provider.remaining_bytes() > 0) {
+    switch (provider.ConsumeEnum<AppendableApi>()) {
+      case AppendableApi::AppendCodeUnit:
+        strAppendable.appendCodeUnit(provider.ConsumeIntegral<char16_t>());
+        break;
+      case AppendableApi::AppendCodePoint:
+        strAppendable.appendCodePoint(provider.ConsumeIntegral<UChar32>());
+        break;
+      case AppendableApi::AppendString: {
+        std::string appendChrs8(
+            provider.ConsumeRandomLengthString(kMaxAppendLength));
+        if (appendChrs8.size() == 0)
+          break;
+        std::vector<char16_t> appendChrs(RandomChar16Array(
+            2, reinterpret_cast<const uint8_t*>(appendChrs8.data()),
+            appendChrs8.size()));
+        strAppendable.appendString(appendChrs.data(), appendChrs.size());
+        break;
+      }
+      case AppendableApi::ReserveAppendCapacity:
+        strAppendable.reserveAppendCapacity(
+            provider.ConsumeIntegralInRange<int32_t>(0, kMaxReserveSize));
+        break;
+      case AppendableApi::GetAppendBuffer: {
+        int32_t out_capacity;
+        const auto min_capacity =
+            provider.ConsumeIntegralInRange<int32_t>(1, kScratchBufSize);
+        char16_t* out_buffer = strAppendable.getAppendBuffer(
+            min_capacity,
+            min_capacity + provider.ConsumeIntegralInRange<int32_t>(
+                               0, kMaxAdditionalDesiredSize),
+            scratch_buf, kScratchBufSize, &out_capacity);
+        // Write arbitrary value at the end of the buffer.
+        if (out_buffer)
+          out_buffer[out_capacity - 1] = 1;
+        break;
+      }
+    }
+  }
+
+  return 0;
+}