Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/bionic/bionic/tests/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 66 kB image not shown  

Quelle  string_test.cpp

  Sprache: C
 

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#define _GNU_SOURCE 1

#include <string.h>

#include <errno.h>
#include <gtest/gtest.h>
#include <malloc.h>
#include <math.h>
#include <stdint.h>
#include <sys/cdefs.h>

#include <algorithm>
#include <array>
#include <concepts>
#include <limits>
#include <numeric>
#include <string>
#include <string_view>
#include <vector>

#include "buffer_tests.h"
#include "utils.h"

#if defined(NOFORTIFY)
#define STRING_TEST string_nofortify
#else
#define STRING_TEST string
#endif

#if defined(__BIONIC__)
#define STRLCPY_SUPPORTED
#define STRLCAT_SUPPORTED
#endif

constexpr auto KB = 1024;
constexpr auto SMALL = 1 * KB;
constexpr auto MEDIUM = 4 * KB;
constexpr auto LARGE = 64 * KB;

static int signum(int i) {
  if (i < 0) {
    return -1;
  } else if (i > 0) {
    return 1;
  }
  return 0;
}

TEST(STRING_TEST, strerror) {
  // Valid.
  ASSERT_STREQ("Success", strerror(0));
  ASSERT_STREQ("Operation not permitted", strerror(1));

  // Invalid.
  ASSERT_STREQ("Unknown error -1", strerror(-1));
  ASSERT_STREQ("Unknown error 134", strerror(EHWPOISON + 1));
}

TEST(STRING_TEST, strerror_l) {
  // bionic just forwards to strerror(3).
  ASSERT_STREQ("Success", strerror_l(0, LC_GLOBAL_LOCALE));
}

#if defined(__BIONIC__)
static void* ConcurrentStrErrorFn(void*) {
  bool equal = (strcmp("Unknown error 2002", strerror(2002)) == 0);
  return reinterpret_cast<void*>(equal);
}
#endif // __BIONIC__

// glibc's strerror isn't thread safe, only its strsignal.
TEST(STRING_TEST, strerror_concurrent) {
#if defined(__BIONIC__)
  const char* strerror1001 = strerror(1001);
  ASSERT_STREQ("Unknown error 1001", strerror1001);

  pthread_t t;
  ASSERT_EQ(0, pthread_create(&t, nullptr, ConcurrentStrErrorFn, nullptr));
  void* result;
  ASSERT_EQ(0, pthread_join(t, &result));
  ASSERT_TRUE(static_cast<bool>(result));

  ASSERT_STREQ("Unknown error 1001", strerror1001);
#else // __BIONIC__
  GTEST_SKIP() << "thread-safe strerror not available";
#endif // __BIONIC__
}

TEST(STRING_TEST, gnu_strerror_r) {
#if !defined(ANDROID_HOST_MUSL)
  char buf[256];

  // Note that glibc doesn't necessarily write into the buffer.

  // Valid.
  ASSERT_STREQ("Success", strerror_r(0, buf, sizeof(buf)));
#if defined(__BIONIC__)
  ASSERT_STREQ("Success", buf);
#endif
  ASSERT_STREQ("Operation not permitted", strerror_r(1, buf, sizeof(buf)));
#if defined(__BIONIC__)
  ASSERT_STREQ("Operation not permitted", buf);
#endif

  // Invalid.
  ASSERT_STREQ("Unknown error -1", strerror_r(-1, buf, sizeof(buf)));
  ASSERT_STREQ("Unknown error -1", buf);
  ASSERT_STREQ("Unknown error 1234", strerror_r(1234, buf, sizeof(buf)));
  ASSERT_STREQ("Unknown error 1234", buf);

  // Buffer too small.
  errno = 0;
  memset(buf, 0sizeof(buf));
  ASSERT_EQ(buf, strerror_r(4567, buf, 2));
  ASSERT_STREQ("U", buf);
  // The GNU strerror_r doesn't set errno (the POSIX one sets it to ERANGE).
  ASSERT_ERRNO(0);
#else
  GTEST_SKIP() << "musl doesn't have GNU strerror_r";
#endif
}

TEST(STRING_TEST, strsignal) {
  // A regular signal.
  ASSERT_STREQ("Hangup", strsignal(1));

  // A real-time signal.
  ASSERT_STREQ("Real-time signal 14", strsignal(SIGRTMIN + 14));
  // One of the signals the C library keeps to itself.
  ASSERT_STREQ("Unknown signal 32", strsignal(32));  // __SIGRTMIN

  // Errors.
  ASSERT_STREQ("Unknown signal -1", strsignal(-1)); // Too small.
  ASSERT_STREQ("Unknown signal 0", strsignal(0)); // Still too small.
  ASSERT_STREQ("Unknown signal 1234", strsignal(1234)); // Too large.
}

static void* ConcurrentStrSignalFn(void*) {
  bool equal = (strcmp("Unknown signal 2002", strsignal(2002)) == 0);
  return reinterpret_cast<void*>(equal);
}

TEST(STRING_TEST, strsignal_concurrent) {
  const char* strsignal1001 = strsignal(1001);
  ASSERT_STREQ("Unknown signal 1001", strsignal1001);

  pthread_t t;
  ASSERT_EQ(0, pthread_create(&t, nullptr, ConcurrentStrSignalFn, nullptr));
  void* result;
  ASSERT_EQ(0, pthread_join(t, &result));
  ASSERT_TRUE(static_cast<bool>(result));

  ASSERT_STREQ("Unknown signal 1001", strsignal1001);
}

// TODO: where did this number come from?
#define ITER        500

// For every length we want to test, vary and change alignment
// of allocated memory, fill it with some values, calculate
// expected result and then run function and compare what we got.
// These tests contributed by Intel Corporation.
// TODO: make these tests more intention-revealing and less random.
template<class Character>
class StringTestState {
 public:
  explicit StringTestState(size_t MAX_LEN) : MAX_LEN(MAX_LEN), align1_index_(0), align2_index_(0) {
    int max_alignment = 64;

    // TODO: fix the tests to not sometimes use twice their specified "MAX_LEN".
    glob_ptr = reinterpret_cast<Character*>(memalign(sysconf(_SC_PAGESIZE), 2 * sizeof(Character) * MAX_LEN + max_alignment));
    glob_ptr1 = reinterpret_cast<Character*>(memalign(sysconf(_SC_PAGESIZE), 2 * sizeof(Character) * MAX_LEN + max_alignment));
    glob_ptr2 = reinterpret_cast<Character*>(memalign(sysconf(_SC_PAGESIZE), 2 * sizeof(Character) * MAX_LEN + max_alignment));

    InitLenArray();

    srandom(1234);
  }

  ~StringTestState() {
    free(glob_ptr);
    free(glob_ptr1);
    free(glob_ptr2);
  }

  void BeginIterations() {
    align1_index_ = 0;
    align2_index_ = 0;

    ResetPointers();
  }

  bool HasNextIteration() {
    return (align1_index_ != (alignments_size - 1) || align2_index_ != (alignments_size - 1));
  }

  void NextIteration() {
    if (align1_index_ == (alignments_size - 1) && align2_index_ == (alignments_size - 1)) {
      return;
    }

    if (align1_index_ == (alignments_size - 1)) {
      align1_index_ = 0;
      align2_index_++;
    } else {
      align1_index_++;
    }

    ResetPointers();
  }

  const size_t MAX_LEN;
  Character *ptr, *ptr1, *ptr2;
  size_t n;
  size_t len[ITER + 1];

 private:
  static size_t alignments[];
  static size_t alignments_size;
  Character *glob_ptr, *glob_ptr1, *glob_ptr2;
  size_t align1_index_, align2_index_;

  // Calculate input lengths and fill state.len with them.
  // Test small lengths with more density than big ones. Manually push
  // smallest (0) and biggest (MAX_LEN) lengths. Avoid repeats.
  // Return number of lengths to test.
  void InitLenArray() {
    n = 0;
    len[n++] = 0;
    for (size_t i = 1; i < ITER; ++i) {
      size_t l = static_cast<size_t>(exp(log(static_cast<double>(MAX_LEN)) * i / ITER));
      if (l != len[n - 1]) {
        len[n++] = l;
      }
    }
    len[n++] = MAX_LEN;
  }

  void ResetPointers() {
    if (align1_index_ == alignments_size || align2_index_ == alignments_size) {
      ptr = ptr1 = ptr2 = nullptr;
    } else {
      ptr = glob_ptr + alignments[align1_index_];
      ptr1 = glob_ptr1 + alignments[align1_index_];
      ptr2 = glob_ptr2 + alignments[align2_index_];
    }
  }
};

template<class Character>
size_t StringTestState<Character>::alignments[] = { 243216480123456711 };

template<class Character>
size_t StringTestState<Character>::alignments_size = sizeof(alignments)/sizeof(size_t);

TEST(STRING_TEST, strcat) {
  StringTestState<char> state(SMALL);
  for (size_t i = 1; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr2, '\2', state.MAX_LEN);
      state.ptr2[state.MAX_LEN - 1] = '\0';
      memcpy(state.ptr, state.ptr2, 2 * state.MAX_LEN);

      memset(state.ptr1, 'L', state.len[i]);
      state.ptr1[random() % state.len[i]] = '\0';
      state.ptr1[state.len[i] - 1] = '\0';

      strcpy(state.ptr + state.MAX_LEN - 1, state.ptr1);

      EXPECT_TRUE(strcat(state.ptr2, state.ptr1) == state.ptr2);
      EXPECT_TRUE(memcmp(state.ptr, state.ptr2, 2 * state.MAX_LEN) == 0);
    }
  }
}

// one byte target with "\0" source
TEST(STRING_TEST, strcpy2) {
  char buf[1];
  char* orig = strdup("");
  ASSERT_EQ(buf, strcpy(buf, orig));
  ASSERT_EQ('\0', buf[0]);
  free(orig);
}

// multibyte target where we under fill target
TEST(STRING_TEST, strcpy3) {
  char buf[10];
  char* orig = strdup("12345");
  memset(buf, 'A'sizeof(buf));
  ASSERT_EQ(buf, strcpy(buf, orig));
  ASSERT_STREQ("12345", buf);
  ASSERT_EQ('A',  buf[6]);
  ASSERT_EQ('A',  buf[7]);
  ASSERT_EQ('A',  buf[8]);
  ASSERT_EQ('A',  buf[9]);
  free(orig);
}

// multibyte target where we fill target exactly
TEST(STRING_TEST, strcpy4) {
  char buf[10];
  char* orig = strdup("123456789");
  memset(buf, 'A'sizeof(buf));
  ASSERT_EQ(buf, strcpy(buf, orig));
  ASSERT_STREQ("123456789", buf);
  free(orig);
}

// one byte target with "\0" source
TEST(STRING_TEST, stpcpy2) {
  char buf[1];
  memset(buf, 'A'sizeof(buf));
  char* orig = strdup("");
  EXPECT_EQ(buf, stpcpy(buf, orig));
  EXPECT_EQ('\0', buf[0]);
  free(orig);
}

// multibyte target where we under fill target
TEST(STRING_TEST, stpcpy3) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  char* orig = strdup("12345");
  EXPECT_EQ(buf+strlen(orig), stpcpy(buf, orig));
  EXPECT_STREQ("12345", buf);
  EXPECT_EQ('A',  buf[6]);
  EXPECT_EQ('A',  buf[7]);
  EXPECT_EQ('A',  buf[8]);
  EXPECT_EQ('A',  buf[9]);
  free(orig);
}

// multibyte target where we fill target exactly
TEST(STRING_TEST, stpcpy4) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  char* orig = strdup("123456789");
  EXPECT_EQ(buf+strlen(orig), stpcpy(buf, orig));
  EXPECT_STREQ("123456789", buf);
  free(orig);
}

TEST(STRING_TEST, strcat2) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  buf[0] = 'a';
  buf[1] = '\0';
  char* res = strcat(buf, "01234");
  ASSERT_EQ(buf, res);
  ASSERT_STREQ("a01234", buf);
  ASSERT_EQ('A',  buf[7]);
  ASSERT_EQ('A',  buf[8]);
  ASSERT_EQ('A',  buf[9]);
}

TEST(STRING_TEST, strcat3) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  buf[0] = 'a';
  buf[1] = '\0';
  char* res = strcat(buf, "01234567");
  ASSERT_EQ(buf, res);
  ASSERT_STREQ("a01234567", buf);
}

TEST(STRING_TEST, strncat2) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  buf[0] = 'a';
  buf[1] = '\0';
  char* res = strncat(buf, "01234"sizeof(buf) - strlen(buf) - 1);
  ASSERT_EQ(buf, res);
  ASSERT_STREQ("a01234", buf);
  ASSERT_EQ('A',  buf[7]);
  ASSERT_EQ('A',  buf[8]);
  ASSERT_EQ('A',  buf[9]);
}

TEST(STRING_TEST, strncat3) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  buf[0] = 'a';
  buf[1] = '\0';
  char* res = strncat(buf, "0123456789"5);
  ASSERT_EQ(buf, res);
  ASSERT_STREQ("a01234", buf);
  ASSERT_EQ('A',  buf[7]);
  ASSERT_EQ('A',  buf[8]);
  ASSERT_EQ('A',  buf[9]);
}

TEST(STRING_TEST, strncat4) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  buf[0] = 'a';
  buf[1] = '\0';
  char* res = strncat(buf, "01234567"8);
  ASSERT_EQ(buf, res);
  ASSERT_STREQ("a01234567", buf);
}

TEST(STRING_TEST, strncat5) {
  char buf[10];
  memset(buf, 'A'sizeof(buf));
  buf[0] = 'a';
  buf[1] = '\0';
  char* res = strncat(buf, "01234567"9);
  ASSERT_EQ(buf, res);
  ASSERT_STREQ("a01234567", buf);
}

TEST(STRING_TEST, strchr_with_0) {
  char buf[10];
  const char* s = "01234";
  memcpy(buf, s, strlen(s) + 1);
  EXPECT_TRUE(strchr(buf, '\0') == (buf + strlen(s)));
}

TEST(STRING_TEST, strchr_multiple) {
  char str[128];
  memset(str, 'a'sizeof(str) - 1);
  str[sizeof(str)-1] = '\0';

  // Verify that strchr finds the first occurrence of 'a' in a string
  // filled with 'a' characters. Iterate over the string putting
  // non 'a' characters at the front of the string during each iteration
  // and continue to verify that strchr can find the first occurrence
  // properly. The idea is to cover all possible alignments of the location
  // of the first occurrence of the 'a' character and which includes
  // other 'a' characters close by.
  for (size_t i = 0; i < sizeof(str) - 1; i++) {
    EXPECT_EQ(&str[i], strchr(str, 'a'));
    str[i] = 'b';
  }
}

template <typename Fn>
  requires std::invocable<Fn, const char*, int>
static void RunStrchrTest(Fn strchr_fn) {
  int seek_char = 'R';

  StringTestState<char> state(SMALL);
  for (size_t i = 1; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      if (~seek_char > 0) {
        memset(state.ptr1, ~seek_char, state.len[i]);
      } else {
        memset(state.ptr1, '\1', state.len[i]);
      }
      state.ptr1[state.len[i] - 1] = '\0';

      size_t pos = random() % state.MAX_LEN;
      char* expected;
      if (pos >= state.len[i] - 1) {
        if (seek_char == 0) {
          expected = state.ptr1 + state.len[i] - 1;
        } else {
          expected = nullptr;
        }
      } else {
        state.ptr1[pos] = seek_char;
        expected = state.ptr1 + pos;
      }

      ASSERT_TRUE(strchr_fn(state.ptr1, seek_char) == expected);
    }
  }
}

TEST(STRING_TEST, strchr) {
  RunStrchrTest([](const char* s, int c) { return strchr(s, c); });
}

#if !defined(NOFORTIFY)
TEST(STRING_TEST, strchr_chk) {
#if defined(__BIONIC__)
  RunStrchrTest([](const char* str, int needle) {
    const size_t l = strlen(str);
    const char* plus_one = __strchr_chk(str, needle, l + 1);
    const char* plus_one_thousand = __strchr_chk(str, needle, l + 1000);
    const char* unknown_size = __strchr_chk(str, needle, size_t(-1));
    EXPECT_EQ(plus_one, plus_one_thousand);
    EXPECT_EQ(plus_one, unknown_size);
    return plus_one;
  });
#else   // __BIONIC__
  GTEST_SKIP() << "strchr_chk tests not available";
#endif  // __BIONIC__
}
#endif  // NOFORTIFY

TEST(STRING_TEST, strchrnul) {
  const char* s = "01234222";
  EXPECT_TRUE(strchrnul(s, '2') == &s[2]);
  EXPECT_TRUE(strchrnul(s, '8') == (s + strlen(s)));
  EXPECT_TRUE(strchrnul(s, '\0') == (s + strlen(s)));
}

TEST(STRING_TEST, strcmp) {
  StringTestState<char> state(SMALL);
  for (size_t i = 1; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, 'v', state.MAX_LEN);
      memset(state.ptr2, 'n', state.MAX_LEN);
      state.ptr1[state.len[i] - 1] = '\0';
      state.ptr2[state.len[i] - 1] = '\0';

      size_t pos = 1 + (random() % (state.MAX_LEN - 1));
      int actual;
      int expected;
      if (pos >= state.len[i] - 1) {
        memcpy(state.ptr1, state.ptr2, state.len[i]);
        expected = 0;
        actual = strcmp(state.ptr1, state.ptr2);
      } else {
        memcpy(state.ptr1, state.ptr2, pos);
        if (state.ptr1[pos] > state.ptr2[pos]) {
          expected = 1;
        } else if (state.ptr1[pos] == state.ptr2[pos]) {
          state.ptr1[pos + 1] = '\0';
          state.ptr2[pos + 1] = '\0';
          expected = 0;
        } else {
          expected = -1;
        }
        actual = strcmp(state.ptr1, state.ptr2);
      }

      ASSERT_EQ(expected, signum(actual));
    }
  }
}

TEST(STRING_TEST, stpcpy) {
  StringTestState<char> state(SMALL);
  for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
    size_t pos = random() % state.MAX_LEN;

    memset(state.ptr1, '\2', pos);
    state.ptr1[pos] = '\0';
    state.ptr1[state.MAX_LEN - 1] = '\0';

    memcpy(state.ptr, state.ptr1, state.MAX_LEN);

    memset(state.ptr2, '\1', state.MAX_LEN);
    state.ptr2[state.MAX_LEN - 1] = '\0';

    memset(state.ptr + state.MAX_LEN, '\1', state.MAX_LEN);
    memcpy(state.ptr + state.MAX_LEN, state.ptr1, pos + 1);
    state.ptr[2 * state.MAX_LEN - 1] = '\0';

    ASSERT_TRUE(stpcpy(state.ptr2, state.ptr1) == state.ptr2 + strlen(state.ptr1));
    ASSERT_FALSE((memcmp(state.ptr1, state.ptr, state.MAX_LEN)) != 0 ||
                 (memcmp(state.ptr2, state.ptr + state.MAX_LEN, state.MAX_LEN) != 0));
  }
}

TEST(STRING_TEST, strcpy) {
  StringTestState<char> state(SMALL);
  for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
    size_t pos = random() % state.MAX_LEN;

    memset(state.ptr1, '\2', pos);
    state.ptr1[pos] = '\0';
    state.ptr1[state.MAX_LEN - 1] = '\0';

    memcpy(state.ptr, state.ptr1, state.MAX_LEN);

    memset(state.ptr2, '\1', state.MAX_LEN);
    state.ptr2[state.MAX_LEN - 1] = '\0';

    memset(state.ptr + state.MAX_LEN, '\1', state.MAX_LEN);
    memcpy(state.ptr + state.MAX_LEN, state.ptr1, pos + 1);
    state.ptr[2 * state.MAX_LEN - 1] = '\0';

    ASSERT_TRUE(strcpy(state.ptr2, state.ptr1) == state.ptr2);
    ASSERT_FALSE((memcmp(state.ptr1, state.ptr, state.MAX_LEN)) != 0 ||
                 (memcmp(state.ptr2, state.ptr + state.MAX_LEN, state.MAX_LEN) != 0));
  }
}

TEST(STRING_TEST, strlcat) {
#if defined(STRLCAT_SUPPORTED)
  StringTestState<char> state(SMALL);
  for (size_t i = 0; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr2, '\2', state.MAX_LEN + state.len[i]);
      state.ptr2[state.MAX_LEN - 1] = '\0';
      memcpy(state.ptr, state.ptr2, state.MAX_LEN + state.len[i]);

      size_t pos = random() % state.MAX_LEN;
      memset(state.ptr1, '\3', pos);
      state.ptr1[pos] = '\0';
      if (pos < state.len[i]) {
        memcpy(state.ptr + state.MAX_LEN - 1, state.ptr1, pos + 1);
      } else {
        memcpy(state.ptr + state.MAX_LEN - 1, state.ptr1, state.len[i]);
        state.ptr[state.MAX_LEN + state.len[i] - 1] = '\0';
      }

      strlcat(state.ptr2, state.ptr1, state.MAX_LEN + state.len[i]);

      ASSERT_TRUE(memcmp(state.ptr, state.ptr2, state.MAX_LEN + state.len[i]) == 0);
    }
  }
#else
  GTEST_SKIP() << "strlcat not available";
#endif
}

TEST(STRING_TEST, strlcpy) {
#if defined(STRLCPY_SUPPORTED)
  StringTestState<char> state(SMALL);
  for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
    int rand = 'O';
    memset(state.ptr1, rand, state.MAX_LEN);

    size_t pos = random() % state.MAX_LEN;
    if (pos < state.MAX_LEN) {
      state.ptr1[pos] = '\0';
    }
    memcpy(state.ptr, state.ptr1, state.MAX_LEN);

    memset(state.ptr2, 'I', state.MAX_LEN);
    memcpy(state.ptr + state.MAX_LEN, state.ptr2, state.MAX_LEN);

    if (pos > state.MAX_LEN - 1) {
      memcpy(state.ptr + state.MAX_LEN, state.ptr1, state.MAX_LEN);
      state.ptr[2 * state.MAX_LEN - 1] = '\0';
    } else {
      memcpy(state.ptr + state.MAX_LEN, state.ptr1, pos + 1);
    }

    ASSERT_EQ(strlcpy(state.ptr2, state.ptr1, state.MAX_LEN), strlen(state.ptr1));
    ASSERT_FALSE((memcmp(state.ptr1, state.ptr, state.MAX_LEN) != 0) ||
                 (memcmp(state.ptr2, state.ptr + state.MAX_LEN, state.MAX_LEN) != 0));
  }
#else
  GTEST_SKIP() << "strlcpy not available";
#endif
}

TEST(STRING_TEST, strncat) {
  StringTestState<char> state(SMALL);
  for (size_t i = 1; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr2, '\2', state.MAX_LEN);
      state.ptr2[state.MAX_LEN - 1] = '\0';
      memcpy(state.ptr, state.ptr2, 2 * state.MAX_LEN);

      memset(state.ptr1, 'I', state.len[i]);
      state.ptr1[random() % state.len[i]] = '\0';
      state.ptr1[state.len[i] - 1] = '\0';

      size_t pos = strlen(state.ptr1);

      size_t actual = random() % state.len[i];
      strncpy(state.ptr + state.MAX_LEN - 1, state.ptr1, std::min(actual, pos));
      state.ptr[state.MAX_LEN + std::min(actual, pos) - 1] = '\0';

      ASSERT_TRUE(strncat(state.ptr2, state.ptr1, actual) == state.ptr2);
      ASSERT_EQ(memcmp(state.ptr, state.ptr2, 2 * state.MAX_LEN), 0);
    }
  }
}

TEST(STRING_TEST, strncmp) {
  StringTestState<char> state(SMALL);
  for (size_t i = 1; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, 'v', state.MAX_LEN);
      memset(state.ptr2, 'n', state.MAX_LEN);
      state.ptr1[state.len[i] - 1] = '\0';
      state.ptr2[state.len[i] - 1] = '\0';

      size_t pos = 1 + (random() % (state.MAX_LEN - 1));
      int actual;
      int expected;
      if (pos >= state.len[i] - 1) {
        memcpy(state.ptr1, state.ptr2, state.len[i]);
        expected = 0;
        actual = strncmp(state.ptr1, state.ptr2, state.len[i]);
      } else {
        memcpy(state.ptr1, state.ptr2, pos);
        if (state.ptr1[pos] > state.ptr2[pos]) {
          expected = 1;
        } else if (state.ptr1[pos] == state.ptr2[pos]) {
          state.ptr1[pos + 1] = '\0';
          state.ptr2[pos + 1] = '\0';
          expected = 0;
        } else {
          expected = -1;
        }
        actual = strncmp(state.ptr1, state.ptr2, state.len[i]);
      }

      ASSERT_EQ(expected, signum(actual));
    }
  }
}

TEST(STRING_TEST, stpncpy) {
  StringTestState<char> state(SMALL);
  for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
    memset(state.ptr1, 'J', state.MAX_LEN);
    // Choose a random size for our src buffer.
    size_t ptr1_len = random() % state.MAX_LEN;
    state.ptr1[ptr1_len] = '\0';
    // Copy ptr1 into ptr, used to verify that ptr1 does not get modified.
    memcpy(state.ptr, state.ptr1, state.MAX_LEN);
    // Init ptr2 to a set value.
    memset(state.ptr2, '\1', state.MAX_LEN);

    // Choose a random amount of data to copy.
    size_t copy_len = random() % state.MAX_LEN;

    // Set the second half of ptr to the expected pattern in ptr2.
    memset(state.ptr + state.MAX_LEN, '\1', state.MAX_LEN);
    memcpy(state.ptr + state.MAX_LEN, state.ptr1, copy_len);
    size_t expected_end;
    if (copy_len > ptr1_len) {
      memset(state.ptr + state.MAX_LEN + ptr1_len, '\0', copy_len - ptr1_len);
      expected_end = ptr1_len;
    } else {
      expected_end = copy_len;
    }

    ASSERT_EQ(state.ptr2 + expected_end, stpncpy(state.ptr2, state.ptr1, copy_len));

    // Verify ptr1 was not modified.
    ASSERT_EQ(0, memcmp(state.ptr1, state.ptr, state.MAX_LEN));
    // Verify ptr2 contains the expected data.
    ASSERT_EQ(0, memcmp(state.ptr2, state.ptr + state.MAX_LEN, state.MAX_LEN));
  }
}

TEST(STRING_TEST, strncpy) {
  StringTestState<char> state(SMALL);
  for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
    // Choose a random value to fill the string, except \0 (string terminator),
    // or \1 (guarantees it's different from anything in ptr2).
    memset(state.ptr1, 'K', state.MAX_LEN);
    // Choose a random size for our src buffer.
    size_t ptr1_len = random() % state.MAX_LEN;
    state.ptr1[ptr1_len] = '\0';
    // Copy ptr1 into ptr, used to verify that ptr1 does not get modified.
    memcpy(state.ptr, state.ptr1, state.MAX_LEN);
    // Init ptr2 to a set value.
    memset(state.ptr2, '\1', state.MAX_LEN);

    // Choose a random amount of data to copy.
    size_t copy_len = random() % state.MAX_LEN;

    // Set the second half of ptr to the expected pattern in ptr2.
    memset(state.ptr + state.MAX_LEN, '\1', state.MAX_LEN);
    memcpy(state.ptr + state.MAX_LEN, state.ptr1, copy_len);
    if (copy_len > ptr1_len) {
      memset(state.ptr + state.MAX_LEN + ptr1_len, '\0', copy_len - ptr1_len);
    }

    ASSERT_EQ(state.ptr2, strncpy(state.ptr2, state.ptr1, copy_len));

    // Verify ptr1 was not modified.
    ASSERT_EQ(0, memcmp(state.ptr1, state.ptr, state.MAX_LEN));
    // Verify ptr2 contains the expected data.
    ASSERT_EQ(0, memcmp(state.ptr2, state.ptr + state.MAX_LEN, state.MAX_LEN));
  }
}

template <typename Fn>
  requires std::invocable<Fn, const char*, int>
static void RunStrrchrTest(Fn strrchr_fn) {
  int seek_char = 'M';
  StringTestState<char> state(SMALL);
  for (size_t i = 1; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      if (~seek_char > 0) {
        memset(state.ptr1, ~seek_char, state.len[i]);
      } else {
        memset(state.ptr1, '\1', state.len[i]);
      }
      state.ptr1[state.len[i] - 1] = '\0';

      size_t pos = random() % state.MAX_LEN;
      char* expected;
      if (pos >= state.len[i] - 1) {
        if (seek_char == 0) {
          expected = state.ptr1 + state.len[i] - 1;
        } else {
          expected = nullptr;
        }
      } else {
        state.ptr1[pos] = seek_char;
        expected = state.ptr1 + pos;
      }

      ASSERT_TRUE(strrchr_fn(state.ptr1, seek_char) == expected);
    }
  }
}

TEST(STRING_TEST, strrchr) {
  RunStrrchrTest([](const char* s, int c) { return strrchr(s, c); });
}

#if !defined(NOFORTIFY)
TEST(STRING_TEST, strrchr_chk) {
#if defined(__BIONIC__)
  RunStrrchrTest([](const char* str, int needle) {
    const size_t l = strlen(str);
    const char* plus_one = __strrchr_chk(str, needle, l + 1);
    const char* plus_one_thousand = __strrchr_chk(str, needle, l + 1000);
    const char* unknown_size = __strrchr_chk(str, needle, size_t(-1));
    EXPECT_EQ(plus_one, plus_one_thousand);
    EXPECT_EQ(plus_one, unknown_size);
    return plus_one;
  });
#else   // __BIONIC__
  GTEST_SKIP() << "strrchr_chk tests not available";
#endif  // __BIONIC__
}
#endif  // NOFORTIFY

TEST(STRING_TEST, memchr) {
  int seek_char = 'N';
  StringTestState<char> state(SMALL);
  for (size_t i = 0; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, ~seek_char, state.len[i]);

      size_t pos = random() % state.MAX_LEN;
      char* expected;
      if (pos >= state.len[i]) {
        expected = nullptr;
      } else {
        state.ptr1[pos] = seek_char;
        expected = state.ptr1 + pos;
      }

      ASSERT_TRUE(memchr(state.ptr1, seek_char, state.len[i]) == expected);
    }
  }
}

TEST(STRING_TEST, memchr_zero) {
  uint8_t* buffer;
  ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&buffer), 6464));
  memset(buffer, 1064);
  ASSERT_TRUE(nullptr == memchr(buffer, 50));
  ASSERT_TRUE(nullptr == memchr(buffer, 100));
}

TEST(STRING_TEST, memrchr) {
  int seek_char = 'P';
  StringTestState<char> state(SMALL);
  for (size_t i = 0; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, ~seek_char, state.len[i]);

      size_t pos = random() % state.MAX_LEN;
      char* expected;
      if (pos >= state.len[i]) {
        expected = nullptr;
      } else {
        state.ptr1[pos] = seek_char;
        expected = state.ptr1 + pos;
      }

      ASSERT_TRUE(memrchr(state.ptr1, seek_char, state.len[i]) == expected);
    }
  }
}

TEST(STRING_TEST, memcmp) {
  StringTestState<char> state(SMALL);
  for (size_t i = 0; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      int c1 = 'A';
      int c2 = 'N';
      memset(state.ptr1, c1, state.MAX_LEN);
      memset(state.ptr2, c1, state.MAX_LEN);

      int pos = (state.len[i] == 0) ? 0 : (random() % state.len[i]);
      state.ptr2[pos] = c2;

      int expected = (static_cast<int>(c1) - static_cast<int>(c2));
      int actual = memcmp(state.ptr1, state.ptr2, state.MAX_LEN);

      ASSERT_EQ(signum(expected), signum(actual));
    }
  }
}

TEST(STRING_TEST, wmemcmp) {
  StringTestState<wchar_t> state(SMALL);

  for (size_t i = 0; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      long long mask = ((long long1 << 8 * sizeof(wchar_t)) - 1;
      int c1 = rand() & mask;
      int c2 = rand() & mask;
      wmemset(state.ptr1, c1, state.MAX_LEN);
      wmemset(state.ptr2, c1, state.MAX_LEN);

      int pos = (state.len[i] == 0) ? 0 : (random() % state.len[i]);
      state.ptr2[pos] = c2;

      int expected = (static_cast<int>(c1) - static_cast<int>(c2));
      int actual = wmemcmp(state.ptr1, state.ptr2, (size_t) state.MAX_LEN);

      ASSERT_EQ(signum(expected), signum(actual));
    }
  }
}

TEST(STRING_TEST, memcpy) {
  StringTestState<char> state(LARGE);
  int rand = 4;
  for (size_t i = 0; i < state.n - 1; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      size_t pos = random() % (state.MAX_LEN - state.len[i]);

      memset(state.ptr1, rand, state.len[i]);
      memset(state.ptr1 + state.len[i], ~rand, state.MAX_LEN - state.len[i]);

      memset(state.ptr2, rand, state.len[i]);
      memset(state.ptr2 + state.len[i], ~rand, state.MAX_LEN - state.len[i]);
      memset(state.ptr2 + pos, '\0', state.len[i]);

      ASSERT_FALSE(memcpy(state.ptr2 + pos, state.ptr1 + pos, state.len[i]) != state.ptr2 + pos);
      ASSERT_EQ(0, memcmp(state.ptr1, state.ptr2, state.MAX_LEN));
    }
  }
}

TEST(STRING_TEST, memset) {
  StringTestState<char> state(LARGE);
  char ch = 'P';
  for (size_t i = 0; i < state.n - 1; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, ~ch, state.MAX_LEN);
      memcpy(state.ptr2, state.ptr1, state.MAX_LEN);

      size_t pos = random () % (state.MAX_LEN - state.len[i]);
      for (size_t k = pos; k < pos + state.len[i]; k++) {
        state.ptr1[k] = ch;
      }

      ASSERT_TRUE(memset(state.ptr2 + pos, ch, state.len[i]) == state.ptr2 + pos);

      ASSERT_EQ(0, memcmp(state.ptr1, state.ptr2, state.MAX_LEN));
    }
  }
}

TEST(STRING_TEST, memmove) {
  StringTestState<char> state(LARGE);
  for (size_t i = 0; i < state.n - 1; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, 'Q'2 * state.MAX_LEN);

      size_t pos = random() % (state.MAX_LEN - state.len[i]);

      memset(state.ptr1, 'R', state.len[i]);
      memcpy(state.ptr2, state.ptr1, 2 * state.MAX_LEN);
      memcpy(state.ptr, state.ptr1, state.len[i]);
      memcpy(state.ptr1 + pos, state.ptr, state.len[i]);

      ASSERT_TRUE(memmove(state.ptr2 + pos, state.ptr2, state.len[i]) == state.ptr2 + pos);
      ASSERT_EQ(0, memcmp(state.ptr2, state.ptr1, 2 * state.MAX_LEN));
    }
  }
}

TEST(STRING_TEST, memmove_cache_size) {
  size_t len = 600000;
  int max_alignment = 31;
  int alignments[] = {05112930};
  char* ptr = reinterpret_cast<char*>(malloc(sizeof(char) * len));
  char* ptr1 = reinterpret_cast<char*>(malloc(2 * sizeof(char) * len));
  char* glob_ptr2 = reinterpret_cast<char*>(malloc(2 * sizeof(char) * len + max_alignment));
  size_t pos = 64;

  ASSERT_TRUE(ptr != nullptr);
  ASSERT_TRUE(ptr1 != nullptr);
  ASSERT_TRUE(glob_ptr2 != nullptr);

  for (int i = 0; i < 5; i++) {
    char* ptr2 = glob_ptr2 + alignments[i];
    memset(ptr1, 'S'2 * len);
    memset(ptr1, 'T', len);
    memcpy(ptr2, ptr1, 2 * len);
    memcpy(ptr, ptr1, len);
    memcpy(ptr1 + pos, ptr, len);

    ASSERT_TRUE(memmove(ptr2 + pos, ptr, len) == ptr2 + pos);
    ASSERT_EQ(0, memcmp(ptr2, ptr1, 2 * len));
  }
  free(ptr);
  free(ptr1);
  free(glob_ptr2);
}

static void verify_memmove(char* src_copy, char* dst, char* src, size_t size) {
  memset(dst, 0, size);
  memcpy(src, src_copy, size);
  ASSERT_EQ(dst, memmove(dst, src, size));
  ASSERT_EQ(0, memcmp(dst, src_copy, size));
}

#define MEMMOVE_DATA_SIZE (1024*1024*3)

TEST(STRING_TEST, memmove_check) {
  char* buffer = reinterpret_cast<char*>(malloc(MEMMOVE_DATA_SIZE));
  ASSERT_TRUE(buffer != nullptr);

  char* src_data = reinterpret_cast<char*>(malloc(MEMMOVE_DATA_SIZE));
  ASSERT_TRUE(src_data != nullptr);
  // Initialize to a known pattern to copy into src for each test and
  // to compare dst against.
  for (size_t i = 0; i < MEMMOVE_DATA_SIZE; i++) {
    src_data[i] = (i + 1) % 255;
  }

  // Check all different dst offsets between 0 and 127 inclusive.
  char* src = buffer;
  for (size_t i = 0; i < 127; i++) {
    char* dst = buffer + 256 + i;
    // Small copy.
    verify_memmove(src_data, dst, src, 1024);

    // Medium copy.
    verify_memmove(src_data, dst, src, 64 * 1024);

    // Medium copy.
    verify_memmove(src_data, dst, src, 1024 * 1024 + 128 * 1024);
  }

  // Check all leftover size offsets between 1 and 127 inclusive.
  char* dst = buffer + 256;
  src = buffer;
  for (size_t size = 1; size < 127; size++) {
    // Small copy.
    verify_memmove(src_data, dst, src, 1024);

    // Medium copy.
    verify_memmove(src_data, dst, src, 64 * 1024);

    // Large copy.
    verify_memmove(src_data, dst, src, 1024 * 1024 + 128 * 1024);
  }
}

TEST(STRING_TEST, bcopy) {
  StringTestState<char> state(LARGE);
  for (size_t i = 0; i < state.n; i++) {
    for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
      memset(state.ptr1, '4', state.MAX_LEN);
      memset(state.ptr1 + state.MAX_LEN, 'a', state.MAX_LEN);
      memcpy(state.ptr2, state.ptr1, 2 * state.MAX_LEN);

      size_t start = random() % (2 * state.MAX_LEN - state.len[i]);
      memcpy(state.ptr2 + start, state.ptr1, state.len[i]);

      bcopy(state.ptr1, state.ptr1 + start, state.len[i]);
      ASSERT_EQ(0, memcmp(state.ptr1, state.ptr2, 2 * state.MAX_LEN));
    }
  }
}

TEST(STRING_TEST, bzero) {
  StringTestState<char> state(LARGE);
  for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
    memset(state.ptr1, 'R', state.MAX_LEN);

    size_t start = random() % state.MAX_LEN;
    size_t end = start + random() % (state.MAX_LEN - start);

    memcpy(state.ptr2, state.ptr1, start);
    memset(state.ptr2 + start, '\0', end - start);
    memcpy(state.ptr2 + end, state.ptr1 + end, state.MAX_LEN - end);

    bzero(state.ptr1 + start, end - start);

    ASSERT_EQ(0, memcmp(state.ptr1, state.ptr2, state.MAX_LEN));
  }
}

static void DoMemcpyTest(uint8_t* src, uint8_t* dst, size_t len) {
  memset(src, (len % 255) + 1, len);
  memset(dst, 0, len);

  ASSERT_EQ(dst, memcpy(dst, src, len));
  ASSERT_TRUE(memcmp(src, dst, len) == 0);
}

TEST(STRING_TEST, memcpy_align) {
  RunSrcDstBufferAlignTest(LARGE, DoMemcpyTest);
}

TEST(STRING_TEST, memcpy_overread) {
  RunSrcDstBufferOverreadTest(DoMemcpyTest);
}

static void DoMemmoveTest(uint8_t* src, uint8_t* dst, size_t len) {
  memset(src, (len % 255) + 1, len);
  memset(dst, 0, len);

  ASSERT_EQ(dst, memmove(dst, src, len));
  ASSERT_TRUE(memcmp(src, dst, len) == 0);
}

TEST(STRING_TEST, memmove_align) {
  RunSrcDstBufferAlignTest(LARGE, DoMemmoveTest);
}

TEST(STRING_TEST, memmove_overread) {
  RunSrcDstBufferOverreadTest(DoMemmoveTest);
}

static void DoMemsetTest(uint8_t* buf, size_t len) {
  for (size_t i = 0; i < len; i++) {
    buf[i] = 0;
  }
  int value = (len % 255) + 1;
  ASSERT_EQ(buf, memset(buf, value, len));
  for (size_t i = 0; i < len; i++) {
    ASSERT_EQ(value, buf[i]);
  }
}

TEST(STRING_TEST, memset_align) {
  RunSingleBufferAlignTest(LARGE, DoMemsetTest);
}

static void DoStrlenTest(uint8_t* buf, size_t len) {
  if (len >= 1) {
    memset(buf, (32 + (len % 96)), len - 1);
    buf[len-1] = '\0';
    ASSERT_EQ(len-1, strlen(reinterpret_cast<char*>(buf)));
  }
}

TEST(STRING_TEST, strlen_align) {
  RunSingleBufferAlignTest(LARGE, DoStrlenTest);
}

TEST(STRING_TEST, strlen_overread) {
  RunSingleBufferOverreadTest(DoStrlenTest);
}

static void DoStrnlenTest(uint8_t* buf, size_t len) {
  if (!len) {
    return;
  }

  auto* s = reinterpret_cast<char*>(buf);
  memset(buf, (32 + (len % 96)), len);
  ASSERT_EQ(len, strnlen(s, len));

  buf[len - 1] = '\0';
  ASSERT_EQ(len - 1, strnlen(s, len));
  ASSERT_EQ(len - 1, strnlen(s, len + 1000));
  ASSERT_EQ(len - 1, strnlen(s, std::numeric_limits<size_t>::max()));
}

TEST(STRING_TEST, strnlen_align) {
  RunSingleBufferAlignTest(LARGE, DoStrnlenTest);
}

TEST(STRING_TEST, strnlen_overread) {
  RunSingleBufferOverreadTest(DoStrnlenTest);
}

static void DoStrcpyTest(uint8_t* src, uint8_t* dst, size_t len) {
  if (len >= 1) {
    memset(src, (32 + (len % 96)), len - 1);
    src[len-1] = '\0';
    memset(dst, 0, len);
    ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcpy(reinterpret_cast<char*>(dst),
                                                     reinterpret_cast<char*>(src))));
    ASSERT_TRUE(memcmp(src, dst, len) == 0);
  }
}

TEST(STRING_TEST, strcpy_align) {
  RunSrcDstBufferAlignTest(LARGE, DoStrcpyTest);
}

TEST(STRING_TEST, strcpy_overread) {
  RunSrcDstBufferOverreadTest(DoStrcpyTest);
}

#if defined(STRLCPY_SUPPORTED)
static void DoStrlcpyTest(uint8_t* src, uint8_t* dst, size_t len) {
  if (len >= 1) {
    memset(src, (32 + (len % 96)), len - 1);
    src[len-1] = '\0';
    memset(dst, 0, len);
    ASSERT_EQ(len-1, strlcpy(reinterpret_cast<char*>(dst),
                             reinterpret_cast<char*>(src), len));
    ASSERT_TRUE(memcmp(src, dst, len) == 0);
  }
}
#endif

TEST(STRING_TEST, strlcpy_align) {
#if defined(STRLCPY_SUPPORTED)
  RunSrcDstBufferAlignTest(LARGE, DoStrlcpyTest);
#else
  GTEST_SKIP() << "strlcpy not available";
#endif
}

TEST(STRING_TEST, strlcpy_overread) {
#if defined(STRLCPY_SUPPORTED)
  RunSrcDstBufferOverreadTest(DoStrlcpyTest);
#else
  GTEST_SKIP() << "strlcpy not available";
#endif
}


static void DoStpcpyTest(uint8_t* src, uint8_t* dst, size_t len) {
  if (len >= 1) {
    memset(src, (32 + (len % 96)), len - 1);
    src[len-1] = '\0';
    memset(dst, 0, len);
    ASSERT_EQ(dst+len-1reinterpret_cast<uint8_t*>(stpcpy(reinterpret_cast<char*>(dst),
                                                           reinterpret_cast<char*>(src))));
    ASSERT_TRUE(memcmp(src, dst, len) == 0);
  }
}

TEST(STRING_TEST, stpcpy_align) {
  RunSrcDstBufferAlignTest(LARGE, DoStpcpyTest);
}

TEST(STRING_TEST, stpcpy_overread) {
  RunSrcDstBufferOverreadTest(DoStpcpyTest);
}

// Use our own incrementer to cut down on the total number of calls.
static size_t LargeSetIncrement(size_t len) {
  if (len >= 4096) {
    return 4096;
  } else if (len >= 1024) {
    return 1024;
  } else if (len >= 256) {
    return 256;
  }
  return 1;
}

#define STRCAT_DST_LEN  64

static void DoStrcatTest(uint8_t* src, uint8_t* dst, size_t len) {
  if (len >= 1) {
    int value = 32 + (len % 96);
    memset(src, value, len - 1);
    src[len-1] = '\0';

    if (len >= STRCAT_DST_LEN) {
      // Create a small buffer for doing quick compares in each loop.
      uint8_t cmp_buf[STRCAT_DST_LEN];
      // Make sure dst string contains a different value then the src string.
      int value2 = 32 + (value + 2) % 96;
      memset(cmp_buf, value2, sizeof(cmp_buf));

      for (size_t i = 1; i <= STRCAT_DST_LEN;) {
        memset(dst, value2, i-1);
        memset(dst+i-10, len-i);
        src[len-i] = '\0';
        ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcat(reinterpret_cast<char*>(dst),
                                                         reinterpret_cast<char*>(src))));
        ASSERT_TRUE(memcmp(dst, cmp_buf, i-1) == 0);
        ASSERT_TRUE(memcmp(src, dst+i-1, len-i+1) == 0);
        // This is an expensive loop, so don't loop through every value,
        // get to a certain size and then start doubling.
        if (i < 16) {
          i++;
        } else {
          i <<= 1;
        }
      }
    } else {
      dst[0] = '\0';
      ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcat(reinterpret_cast<char*>(dst),
                                                       reinterpret_cast<char*>(src))));
      ASSERT_TRUE(memcmp(src, dst, len) == 0);
    }
  }
}

TEST(STRING_TEST, strcat_align) {
  RunSrcDstBufferAlignTest(MEDIUM, DoStrcatTest, LargeSetIncrement);
}

TEST(STRING_TEST, strcat_overread) {
  RunSrcDstBufferOverreadTest(DoStrcatTest);
}

#if defined(STRLCAT_SUPPORTED)
static void DoStrlcatTest(uint8_t* src, uint8_t* dst, size_t len) {
  if (len >= 1) {
    int value = 32 + (len % 96);
    memset(src, value, len - 1);
    src[len-1] = '\0';

    if (len >= STRCAT_DST_LEN) {
      // Create a small buffer for doing quick compares in each loop.
      uint8_t cmp_buf[STRCAT_DST_LEN];
      // Make sure dst string contains a different value then the src string.
      int value2 = 32 + (value + 2) % 96;
      memset(cmp_buf, value2, sizeof(cmp_buf));

      for (size_t i = 1; i <= STRCAT_DST_LEN;) {
        memset(dst, value2, i-1);
        memset(dst+i-10, len-i);
        src[len-i] = '\0';
        ASSERT_EQ(len-1, strlcat(reinterpret_cast<char*>(dst),
                                 reinterpret_cast<char*>(src), len));
        ASSERT_TRUE(memcmp(dst, cmp_buf, i-1) == 0);
        ASSERT_TRUE(memcmp(src, dst+i-1, len-i+1) == 0);
        // This is an expensive loop, so don't loop through every value,
        // get to a certain size and then start doubling.
        if (i < 16) {
          i++;
        } else {
          i <<= 1;
        }
      }
    } else {
      dst[0] = '\0';
      ASSERT_EQ(len-1, strlcat(reinterpret_cast<char*>(dst),
                               reinterpret_cast<char*>(src), len));
      ASSERT_TRUE(memcmp(src, dst, len) == 0);
    }
  }
}
#endif

TEST(STRING_TEST, strlcat_align) {
#if defined(STRLCAT_SUPPORTED)
  RunSrcDstBufferAlignTest(MEDIUM, DoStrlcatTest, LargeSetIncrement);
#else
  GTEST_SKIP() << "strlcat not available";
#endif
}

TEST(STRING_TEST, strlcat_overread) {
#if defined(STRLCAT_SUPPORTED)
  RunSrcDstBufferOverreadTest(DoStrlcatTest);
#else
  GTEST_SKIP() << "strlcat not available";
#endif
}

static void DoStrcmpTest(uint8_t* buf1, uint8_t* buf2, size_t len) {
  if (len >= 1) {
    memset(buf1, (32 + (len % 96)), len - 1);
    buf1[len-1] = '\0';
    memset(buf2, (32 + (len % 96)), len - 1);
    buf2[len-1] = '\0';
    ASSERT_EQ(0, strcmp(reinterpret_cast<char*>(buf1),
                        reinterpret_cast<char*>(buf2)));
  }
}

static void DoStrcmpFailTest(uint8_t* buf1, uint8_t* buf2, size_t len1, size_t len2) {
  // Do string length differences.
  int c = (32 + (len1 % 96));
  memset(buf1, c, len1 - 1);
  buf1[len1-1] = '\0';
  memset(buf2, c, len2 - 1);
  buf2[len2-1] = '\0';
  ASSERT_NE(0, strcmp(reinterpret_cast<char*>(buf1),
                      reinterpret_cast<char*>(buf2)));

  // Do single character differences.
  size_t len;
  if (len1 > len2) {
    len = len2;
  } else {
    len = len1;
  }
  // Need at least a two character buffer to do this test.
  if (len > 1) {
    buf1[len-1] = '\0';
    buf2[len-1] = '\0';
    int diff_c = (c + 1) % 96;

    buf1[len-2] = diff_c;
    ASSERT_NE(0, strcmp(reinterpret_cast<char*>(buf1),
                        reinterpret_cast<char*>(buf2)));

    buf1[len-2] = c;
    buf2[len-2] = diff_c;
    ASSERT_NE(0, strcmp(reinterpret_cast<char*>(buf1),
                        reinterpret_cast<char*>(buf2)));
  }
}

TEST(STRING_TEST, strcmp_align) {
  RunCmpBufferAlignTest(MEDIUM, DoStrcmpTest, DoStrcmpFailTest, LargeSetIncrement);
}

TEST(STRING_TEST, strcmp_overread) {
  RunCmpBufferOverreadTest(DoStrcmpTest, DoStrcmpFailTest);
}

static void DoMemcmpTest(uint8_t* buf1, uint8_t* buf2, size_t len) {
  memset(buf1, len+1, len);
  memset(buf2, len+1, len);
  ASSERT_EQ(0, memcmp(buf1, buf2, len));
}

static void DoMemcmpFailTest(uint8_t* buf1, uint8_t* buf2, size_t len1, size_t len2) {
  size_t len;
  if (len1 > len2) {
    len = len2;
  } else {
    len = len1;
  }

  memset(buf1, len2+1, len);
  buf1[len-1] = len2;
  memset(buf2, len2+1, len);
  ASSERT_NE(0, memcmp(buf1, buf2, len));

  buf1[len-1] = len2+1;
  buf2[len-1] = len2;
  ASSERT_NE(0, memcmp(buf1, buf2, len));
}

TEST(STRING_TEST, memcmp_align) {
  RunCmpBufferAlignTest(MEDIUM, DoMemcmpTest, DoMemcmpFailTest, LargeSetIncrement);
}

TEST(STRING_TEST, memcmp_overread) {
  RunCmpBufferOverreadTest(DoMemcmpTest, DoMemcmpFailTest);
}

static void DoMemchrTest(uint8_t* buf, size_t len) {
  if (len >= 1) {
    int value = len % 128;
    int search_value = (len % 128) + 1;
    memset(buf, value, len);
    // The buffer does not contain the search value.
    ASSERT_EQ(nullptr, memchr(buf, search_value, len));
    if (len >= 2) {
      buf[0] = search_value;
      // The search value is the first element in the buffer.
      ASSERT_EQ(&buf[0], memchr(buf, search_value, len));

      buf[0] = value;
    }

    // The search value is the last element in the buffer.
    buf[len - 1] = search_value;
    ASSERT_EQ(&buf[len - 1], memchr(buf, search_value, len));

    // The search value is the last element in the buffer, and the length
    // spans well beyond the buffer's end. C11 explicitly allows this.
    ASSERT_EQ(&buf[len - 1], memchr(buf, search_value, len + 4096));
  }
}

TEST(STRING_TEST, memchr_align) {
  RunSingleBufferAlignTest(MEDIUM, DoMemchrTest);
}

TEST(STRING_TEST, memchr_overread) {
  RunSingleBufferOverreadTest(DoMemchrTest);
}

template <typename Fn>
  requires std::invocable<Fn, const char*, int>
static void DoStrchrTestImpl(uint8_t* buf, size_t len, Fn strchr_fn) {
  if (len >= 1) {
    char value = 32 + (len % 96);
    char search_value = 33 + (len % 96);
    memset(buf, value, len - 1);
    buf[len - 1] = '\0';
    // The buffer does not contain the search value.
    ASSERT_EQ(nullptr, strchr_fn(reinterpret_cast<char*>(buf), search_value));
    // Search for the special '\0' character.
    ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 1]),
              strchr_fn(reinterpret_cast<char*>(buf), '\0'));
    if (len >= 2) {
      buf[0] = search_value;
      // The search value is the first element in the buffer.
      ASSERT_EQ(reinterpret_cast<char*>(&buf[0]),
                strchr_fn(reinterpret_cast<char*>(buf), search_value));

      buf[0] = value;
      buf[len - 2] = search_value;
      // The search value is the second to last element in the buffer.
      // The last element is the '\0' character.
      ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 2]),
                strchr_fn(reinterpret_cast<char*>(buf), search_value));
    }
  }
}

static void DoStrchrTest(uint8_t* buf, size_t len) {
  DoStrchrTestImpl(buf, len, [](const char* s, int c) { return strchr(s, c); });
}

TEST(STRING_TEST, strchr_align) {
  RunSingleBufferAlignTest(MEDIUM, DoStrchrTest);
}

TEST(STRING_TEST, strchr_overread) {
  RunSingleBufferOverreadTest(DoStrchrTest);
}

template <typename Fn>
  requires std::invocable<Fn, const char*, int>
static void DoStrrchrTestImpl(uint8_t* buf, size_t len, Fn strrchr_fn) {
  if (len >= 1) {
    char value = 32 + (len % 96);
    char search_value = 33 + (len % 96);
    memset(buf, value, len - 1);
    buf[len - 1] = '\0';
    // The buffer does not contain the search value.
    ASSERT_EQ(nullptr, strrchr_fn(reinterpret_cast<char*>(buf), search_value));
    // Search for the special '\0' character.
    ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 1]),
              strrchr_fn(reinterpret_cast<char*>(buf), '\0'));
    if (len >= 2) {
      buf[0] = search_value;
      // The search value is the first element in the buffer.
      ASSERT_EQ(reinterpret_cast<char*>(&buf[0]),
                strrchr_fn(reinterpret_cast<char*>(buf), search_value));

      buf[0] = value;
      buf[len - 2] = search_value;
      // The search value is the second to last element in the buffer.
      // The last element is the '\0' character.
      ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 2]),
                strrchr_fn(reinterpret_cast<char*>(buf), search_value));
    }
  }
}

static void DoStrrchrTest(uint8_t* buf, size_t len) {
  DoStrrchrTestImpl(buf, len, [](const char* s, int c) { return strrchr(s, c); });
}

TEST(STRING_TEST, strrchr_align) {
  RunSingleBufferAlignTest(MEDIUM, DoStrrchrTest);
}

TEST(STRING_TEST, strrchr_overread) {
  RunSingleBufferOverreadTest(DoStrrchrTest);
}

#if !defined(NOFORTIFY)
#if defined(__BIONIC__)
static void DoStrchrChkTest(uint8_t* buf, size_t len) {
  DoStrchrTestImpl(buf, len, [len](const char* s, int c) { return __strchr_chk(s, c, len); });
}
#endif  // __BIONIC__

TEST(STRING_TEST, strchr_chk_align) {
#if defined(__BIONIC__)
  RunSingleBufferAlignTest(MEDIUM, DoStrchrChkTest);
#else   // __BIONIC__
  GTEST_SKIP() << "strchr_chk_align tests not available";
#endif  // __BIONIC__
}

TEST(STRING_TEST, strchr_chk_overread) {
#if defined(__BIONIC__)
  RunSingleBufferOverreadTest(DoStrchrChkTest);
#else   // __BIONIC__
  GTEST_SKIP() << "strchr_chk_overread tests not available";
#endif  // __BIONIC__
}

#if defined(__BIONIC__)
static void DoStrrchrChkTest(uint8_t* buf, size_t len) {
  DoStrrchrTestImpl(buf, len, [len](const char* s, int c) { return __strrchr_chk(s, c, len); });
}
#endif  // __BIONIC__

TEST(STRING_TEST, strrchr_chk_align) {
#if defined(__BIONIC__)
  RunSingleBufferAlignTest(MEDIUM, DoStrrchrChkTest);
#else   // __BIONIC__
  GTEST_SKIP() << "strrchr_chk_align tests not available";
#endif  // __BIONIC__
}

TEST(STRING_TEST, strrchr_chk_overread) {
#if defined(__BIONIC__)
  RunSingleBufferOverreadTest(DoStrrchrChkTest);
#else   // __BIONIC__
  GTEST_SKIP() << "strrchr_chk_overread tests not available";
#endif  // __BIONIC__
}
#endif  // !NOFORTIFY

#if !defined(NOFORTIFY)
TEST(STRING_TEST, strchr_chk_bounds) {
#if defined(__BIONIC__)
  char buf[32];
  memset(buf, 'A'sizeof(buf) - 1);
  buf[31] = '\0';

  buf[16] = 'X';
  EXPECT_DEATH(__strchr_chk(buf, 'X'16), "strchr: prevented read past end of buffer");
  EXPECT_EQ(__strchr_chk(buf, 'X'17), buf + 16);

  buf[16] = 'A';
  buf[20] = 'X';
  EXPECT_DEATH(__strchr_chk(buf, 'X'16), "strchr: prevented read past end of buffer");

  buf[20] = 'A';
  buf[16] = '\0';
  EXPECT_DEATH(__strchr_chk(buf, 'X'16), "strchr: prevented read past end of buffer");
  EXPECT_EQ(__strchr_chk(buf, 'X'17), nullptr);
#else   // __BIONIC__
  GTEST_SKIP() << "strchr_chk_bounds tests not available";
#endif  // __BIONIC__
}
#endif  // !NOFORTIFY

#if !defined(NOFORTIFY)
TEST(STRING_TEST, strrchr_chk_bounds) {
#if defined(__BIONIC__)
  char buf[32];
  memset(buf, 'A'sizeof(buf));
  buf[31] = '\0';

  buf[16] = 'X';
  buf[17] = '\0';
  EXPECT_DEATH(__strrchr_chk(buf, 'X'17), "strrchr: prevented read past end of buffer");
  EXPECT_EQ(__strrchr_chk(buf, 'X'18), buf + 16);
  buf[16] = 'A';
  buf[17] = 'A';
  buf[20] = 'X';
  EXPECT_DEATH(__strrchr_chk(buf, 'X'16), "strrchr: prevented read past end of buffer");

  buf[20] = 'A';
  buf[16] = '\0';
  EXPECT_DEATH(__strrchr_chk(buf, 'X'16), "strrchr: prevented read past end of buffer");
  EXPECT_EQ(__strrchr_chk(buf, 'X'17), nullptr);
#else   // __BIONIC__
  GTEST_SKIP() << "strrchr_chk_bounds tests not available";
#endif  // __BIONIC__
}
#endif  // !NOFORTIFY

#if !defined(ANDROID_HOST_MUSL)
static void TestBasename(const char* in, const char* expected_out) {
  errno = 0;
  const char* out = basename(in);
  ASSERT_STREQ(expected_out, out) << in;
  ASSERT_ERRNO(0) << in;
}
#endif

TEST(STRING_TEST, __gnu_basename) {
#if !defined(ANDROID_HOST_MUSL)
  TestBasename("""");
  TestBasename("/usr/lib""lib");
  TestBasename("/usr/""");
  TestBasename("usr""usr");
  TestBasename("/""");
  TestBasename("."".");
  TestBasename("..""..");
  TestBasename("///", "");
  TestBasename("//usr//lib//", "");
#else
  GTEST_SKIP() << "musl doesn't have GNU basename";
#endif
}

TEST(STRING_TEST, strnlen_147048) {
  // https://code.google.com/p/android/issues/detail?id=147048
  char stack_src[64] = {0};
  EXPECT_EQ(0U, strnlen(stack_src, 1024*1024*1024));
  char* heap_src = new char[1];
  *heap_src = '\0';
  EXPECT_EQ(0U, strnlen(heap_src, 1024*1024*1024));
  delete[] heap_src;
}

TEST(STRING_TEST, strnlen_74741) {
  ASSERT_EQ(4U, strnlen("test", SIZE_MAX));
}

TEST(STRING_TEST, mempcpy) {
  char dst[6];
  ASSERT_EQ(&dst[4], reinterpret_cast<char*>(mempcpy(dst, "hello"4)));
}

// clang depends on the fact that a memcpy where src and dst is the same
// still operates correctly. This test verifies that this assumption
// holds true.
// See https://llvm.org/bugs/show_bug.cgi?id=11763 for more information.
static std::vector<uint8_t> g_memcpy_same_buffer;

static void DoMemcpySameTest(uint8_t* buffer, size_t len) {
  memcpy(buffer, g_memcpy_same_buffer.data(), len);
  ASSERT_EQ(buffer, memcpy(buffer, buffer, len));
  ASSERT_TRUE(memcmp(buffer, g_memcpy_same_buffer.data(), len) == 0);
}

TEST(STRING_TEST, memcpy_src_dst_same) {
  g_memcpy_same_buffer.resize(MEDIUM);
  for (size_t i = 0; i < MEDIUM; i++) {
    g_memcpy_same_buffer[i] = i;
  }
  RunSingleBufferAlignTest(MEDIUM, DoMemcpySameTest);
}

TEST(STRING_TEST, memmem_strstr_empty_needle) {
  const char* some_haystack = "haystack";
  const char* empty_haystack = "";

  ASSERT_EQ(some_haystack, memmem(some_haystack, 8""0));
  ASSERT_EQ(empty_haystack, memmem(empty_haystack, 0""0));

  ASSERT_EQ(some_haystack, strstr(some_haystack, ""));
  ASSERT_EQ(empty_haystack, strstr(empty_haystack, ""));
}

TEST(STRING_TEST, memmem_smoke) {
  const char haystack[] = "big\0daddy/giant\0haystacks!";

  // The current memmem() implementation has special cases for needles of
  // lengths 0, 1, 2, 3, and 4, plus a long needle case. We test matches at the
  // beginning, middle, and end of the haystack.

  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), ""0));

  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "b"1));
  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "bi"2));
  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "big"3));
  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "big\0"4));
  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "big\0d"5));

  ASSERT_EQ(haystack + 2, memmem(haystack, sizeof(haystack), "g"1));
  ASSERT_EQ(haystack + 10, memmem(haystack, sizeof(haystack), "gi"2));
  ASSERT_EQ(haystack + 10, memmem(haystack, sizeof(haystack), "gia"3));
  ASSERT_EQ(haystack + 10, memmem(haystack, sizeof(haystack), "gian"4));
  ASSERT_EQ(haystack + 10, memmem(haystack, sizeof(haystack), "giant"5));

  ASSERT_EQ(haystack + 25, memmem(haystack, sizeof(haystack), "!"1));
  ASSERT_EQ(haystack + 24, memmem(haystack, sizeof(haystack), "s!"2));
  ASSERT_EQ(haystack + 23, memmem(haystack, sizeof(haystack), "ks!"3));
  ASSERT_EQ(haystack + 22, memmem(haystack, sizeof(haystack), "cks!"4));
  ASSERT_EQ(haystack + 21, memmem(haystack, sizeof(haystack), "acks!"5));
}

TEST(STRING_TEST, strstr_smoke) {
  const char* haystack = "big daddy/giant haystacks!";

  // The current strstr() implementation has special cases for needles of
  // lengths 0, 1, 2, 3, and 4, plus a long needle case. We test matches at the
  // beginning, middle, and end of the haystack.

  ASSERT_EQ(haystack + 0, strstr(haystack, ""));

  ASSERT_EQ(haystack + 0, strstr(haystack, "b"));
  ASSERT_EQ(haystack + 0, strstr(haystack, "bi"));
  ASSERT_EQ(haystack + 0, strstr(haystack, "big"));
  ASSERT_EQ(haystack + 0, strstr(haystack, "big "));
  ASSERT_EQ(haystack + 0, strstr(haystack, "big d"));

  ASSERT_EQ(haystack + 2, strstr(haystack, "g"));
  ASSERT_EQ(haystack + 10, strstr(haystack, "gi"));
  ASSERT_EQ(haystack + 10, strstr(haystack, "gia"));
  ASSERT_EQ(haystack + 10, strstr(haystack, "gian"));
  ASSERT_EQ(haystack + 10, strstr(haystack, "giant"));

  ASSERT_EQ(haystack + 25, strstr(haystack, "!"));
  ASSERT_EQ(haystack + 24, strstr(haystack, "s!"));
  ASSERT_EQ(haystack + 23, strstr(haystack, "ks!"));
  ASSERT_EQ(haystack + 22, strstr(haystack, "cks!"));
  ASSERT_EQ(haystack + 21, strstr(haystack, "acks!"));
}

TEST(STRING_TEST, strcasestr_smoke) {
  const char* haystack = "bIg dAdDy/gIaNt hAyStAcKs";
  ASSERT_EQ(haystack, strcasestr(haystack, ""));
  ASSERT_EQ(haystack + 0, strcasestr(haystack, "B"));
  ASSERT_EQ(haystack + 1, strcasestr(haystack, "i"));
  ASSERT_EQ(haystack + 4, strcasestr(haystack, "Da"));
}

TEST(STRING_TEST, strcasestr_empty) {
  const char* empty_haystack = "";
  ASSERT_EQ(empty_haystack, strcasestr(empty_haystack, ""));
}

TEST(STRING_TEST, strcoll_smoke) {
  ASSERT_TRUE(strcoll("aab""aac") < 0);
  ASSERT_TRUE(strcoll("aab""aab") == 0);
  ASSERT_TRUE(strcoll("aac""aab") > 0);
}

TEST(STRING_TEST, strcoll_l_smoke) {
  // bionic just forwards to strcoll(3).
  ASSERT_TRUE(strcoll_l("aab""aac", LC_GLOBAL_LOCALE) < 0);
  ASSERT_TRUE(strcoll_l("aab""aab", LC_GLOBAL_LOCALE) == 0);
  ASSERT_TRUE(strcoll_l("aac""aab", LC_GLOBAL_LOCALE) > 0);
}

TEST(STRING_TEST, strxfrm_smoke) {
  const char* src1 = "aab";
  char dst1[16] = {};
  // Dry run.
  ASSERT_EQ(strxfrm(dst1, src1, 0), 3U);
  ASSERT_STREQ(dst1, "");
  // Really do it.
  ASSERT_EQ(strxfrm(dst1, src1, sizeof(dst1)), 3U);

  const char* src2 = "aac";
  char dst2[16] = {};
  // Dry run.
  ASSERT_EQ(strxfrm(dst2, src2, 0), 3U);
  ASSERT_STREQ(dst2, "");
  // Really do it.
  ASSERT_EQ(strxfrm(dst2, src2, sizeof(dst2)), 3U);

  // The "transform" of two different strings should cause different outputs.
  ASSERT_TRUE(strcmp(dst1, dst2) < 0);
}

TEST(STRING_TEST, strxfrm_l_smoke) {
  // bionic just forwards to strxfrm(3), so this is a subset of the
  // strxfrm test.
  const char* src1 = "aab";
  char dst1[16] = {};
  ASSERT_EQ(strxfrm_l(dst1, src1, 0, LC_GLOBAL_LOCALE), 3U);
  ASSERT_STREQ(dst1, "");
  ASSERT_EQ(strxfrm_l(dst1, src1, sizeof(dst1), LC_GLOBAL_LOCALE), 3U);
}

TEST(STRING_TEST, memccpy_smoke) {
  char dst[32];

  memset(dst, 0sizeof(dst));
  char* p = static_cast<char*>(memccpy(dst, "hello world"' '32));
  EXPECT_STREQ("hello ", dst);
  EXPECT_EQ(ptrdiff_t(6), p - dst);

  memset(dst, 0sizeof(dst));
  EXPECT_EQ(nullptr, memccpy(dst, "hello world"' '4));
  EXPECT_STREQ("hell", dst);
}

TEST(STRING_TEST, memset_explicit_smoke) {
#if defined(__BIONIC__)
  // We can't reliably test that the compiler won't optimize out calls to
  // memset_explicit(), but we can at least check that it behaves like memset.
  char buf[32];
  memset_explicit(buf, 'x'sizeof(buf));
  ASSERT_TRUE(memcmp(buf, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"sizeof(buf)) == 0);
#else
  GTEST_SKIP() << "memset_explicit not available";
#endif
}

TEST(STRING_TEST, strerrorname_np) {
#if defined(__BIONIC__)
  ASSERT_STREQ("0", strerrorname_np(0));
  ASSERT_STREQ("EINVAL", strerrorname_np(EINVAL));
  ASSERT_STREQ("ENOSYS", strerrorname_np(ENOSYS));

  ASSERT_EQ(nullptr, strerrorname_np(-1));
  ASSERT_EQ(nullptr, strerrorname_np(666));
#else
  GTEST_SKIP() << "strerrorname_np not available";
#endif
}

TEST(STRING_TEST, strpbrk) {
  EXPECT_EQ(nullptr, strpbrk("hello"""));
  EXPECT_STREQ("hello", strpbrk("hello""ehl"));
  EXPECT_STREQ("llo", strpbrk("hello""l"));
  EXPECT_STREQ(" world", strpbrk("hello world""\t "));

  // Check that the implementation copes with top bit set characters.
  EXPECT_STREQ("\x80world", strpbrk("hello\x80world""\x80 "));
}

TEST(STRING_TEST, strspn) {
  EXPECT_EQ(0u, strspn("hello"""));
  EXPECT_EQ(4u, strspn("hello""ehl"));
  EXPECT_EQ(5u, strspn("hello""ehlo"));
  EXPECT_EQ(5u, strspn("hello world""abcdefghijklmnopqrstuvwxyz"));

  // Check that the implementation copes with top bit set characters.
  EXPECT_EQ(6u, strspn("hello\x80world""helo\x80rld"));
}

static void DoStrspnTest(uint8_t* raw_buf, size_t len) {
  if (!len) {
    return;
  }

  char* buf = reinterpret_cast<char*>(raw_buf);

  if (len == 1) {
    buf[0] = '\0';
    EXPECT_EQ(0ul, strspn(buf, "abc"));
    EXPECT_EQ(0ul, strspn("abc", buf));
    return;
  }

  memset(buf, 'a', len - 1);
  buf[len - 1] = '\0';
  EXPECT_EQ(strspn(buf, buf), len - 1);
}

TEST(STRING_TEST, strspn_overread) {
  RunSingleBufferOverreadTest(DoStrspnTest);
}

TEST(STRING_TEST, strcspn) {
  EXPECT_EQ(5u, strcspn("hello"""));
  EXPECT_EQ(0u, strcspn("hello""ehl"));
  EXPECT_EQ(5u, strcspn("hello""abc"));
  EXPECT_EQ(5u, strcspn("hello world"" "));

  // Check that the implementation copes with top bit set characters.
  EXPECT_EQ(5u, strcspn("hello\x80world""\x80"));
}

static void DoStrcspnTest(uint8_t* raw_buf, size_t len) {
  if (len <= 2) {
    return;
  }

  char* buf = reinterpret_cast<char*>(raw_buf);

  // Split the buffer into two equal parts with different contents, and then
  // run both configurations on strcspn. This should provide sufficient variety
  // in sizes.
  const size_t split_point = len / 2;
  char* buf_1_start = buf;
  char* buf_1_nul = buf + split_point;
  char* buf_2_start = buf_1_nul + 1;
  char* buf_2_nul = buf + len - 1;

  memset(buf_1_start, 'a', buf_1_nul - buf_1_start);
  *buf_1_nul = '\0';
  memset(buf_2_start, 'b', buf_2_nul - buf_2_start);
  *buf_2_nul = '\0';

  EXPECT_EQ(strcspn(buf_2_start, buf_1_start), len - split_point - 2);
  EXPECT_EQ(strcspn(buf_1_start, buf_2_start), split_point);
}

TEST(STRING_TEST, strcspn_overread) {
  RunSingleBufferOverreadTest(DoStrcspnTest);
}

// Haystack size classes for large needle test functions; see comments in those
// for how these were determined.
constexpr auto STRSPN_HAYSTACK_SIZE_CLASSES = {1664128192256};

// Returns a string of 0x01, 0x02, 0x03, ... 0xFF
static std::string StringOfAllChars() {
  std::string chars(255'a');
  std::iota(chars.begin(), chars.end(), 1);
  return chars;
}

static std::string MustReplaceChar(std::string_view haystack, char replace_what,
                                   char replace_with) {
  std::string s{haystack};
  auto i = s.find(replace_what);
  if (i == std::string::npos) {
    fprintf(stderr, "Fatal: no '%c' in given string.\n", replace_what);
    abort();
  }
  s[i] = replace_with;
  return s;
}

TEST(STRING_TEST, strspn_large_needle_many_haystack_sizes) {
  // This test was built with psimd's strspn impl in mind, which is written:
  // - with one path for keep/reject sets of <= 4 chars, falling back to
  // - one path for haystacks <= 128B, falling back to
  // - one general path for all other cases
  //
  // The 'general path for all other cases' also branches based on whether the
  // keep/reject set has any chars >= 0x80.
  //
  // Most existing handwritten tests fall into the "<= 4 chars keep/reject set"
  // path, so this ignores that.
  const std::string all_chars = StringOfAllChars();
  for (size_t size_class : STRSPN_HAYSTACK_SIZE_CLASSES) {
    std::string haystack(size_class, 'a');
    EXPECT_EQ(strspn(haystack.c_str(), all_chars.c_str()), size_class);

    auto test_back_off_from_end = [&](char target, size_t set_size = 0) {
      const char not_target = target + 1;
      std::fill(haystack.begin(), haystack.end(), not_target);

      std::string all_chars_except_target = MustReplaceChar(all_chars, target, not_target);
      if (set_size) {
        all_chars_except_target.resize(set_size);
      }

      for (size_t i = 1; i < 16; ++i) {
        char& replace_char = haystack[haystack.size() - i];
        char old_char = replace_char;
        replace_char = target;
        EXPECT_EQ(strspn(haystack.c_str(), all_chars_except_target.c_str()), size_class - i);
        replace_char = old_char;
      }
    };

    test_back_off_from_end('a');
    test_back_off_from_end('a'/*set_size=*/127);
    test_back_off_from_end(0xA0);
  }
}

TEST(STRING_TEST, strcspn_large_needle_many_haystack_sizes) {
  // This test was built with psimd's strcspn impl in mind, which is written:
  // strspn, so the same special cases apply:
  // - with one path for keep/reject sets of <= 4 chars, falling back to
  // - one path for haystacks <= 128B, falling back to
  // - one general path for all other cases
  //
  // The 'general path for all other cases' also branches based on whether the
  // keep/reject set has any chars >= 0x80.
  //
  // Most existing handwritten tests fall into the "<= 4 chars keep/reject set"
  // path, so this ignores that.
  const std::string all_chars = StringOfAllChars();
  for (size_t size_class : STRSPN_HAYSTACK_SIZE_CLASSES) {
    std::string haystack(size_class, 'a');

    auto test_back_off_from_end = [&](char target, size_t set_size = 0) {
      const char not_target = target + 1;
      std::fill(haystack.begin(), haystack.end(), target);

      std::string all_chars_except_target = MustReplaceChar(all_chars, target, not_target);
      if (set_size) {
        all_chars_except_target.resize(set_size);
      }

      EXPECT_EQ(strcspn(haystack.c_str(), all_chars_except_target.c_str()), size_class);
      for (size_t i = 1; i < 16; ++i) {
        char& replace_char = haystack[haystack.size() - i];
        char old_char = replace_char;
        replace_char = not_target;
        EXPECT_EQ(strcspn(haystack.c_str(), all_chars_except_target.c_str()), size_class - i);
        replace_char = old_char;
      }
    };

    test_back_off_from_end('a');
    test_back_off_from_end('a'/*set_size=*/127);
    test_back_off_from_end(0xA0);
  }
}

TEST(STRING_TEST, strsep) {
  char* p = nullptr;
  EXPECT_EQ(nullptr, strsep(&p, ":"));

  // Unlike strtok(), strsep() _does_ return empty strings.
  char str[] = ":hello:world:::foo:";
  p = str;
  EXPECT_STREQ("", strsep(&p, ":"));
  EXPECT_STREQ("hello", strsep(&p, ":"));
  EXPECT_STREQ("world", strsep(&p, ":"));
  EXPECT_STREQ("", strsep(&p, ":"));
  EXPECT_STREQ("", strsep(&p, ":"));
  EXPECT_STREQ("foo", strsep(&p, ":"));
  EXPECT_STREQ("", strsep(&p, ":"));
  EXPECT_EQ(nullptr, strsep(&p, ":"));
  // Repeated calls after the first nullptr keep returning nullptr.
  for (size_t i = 0; i < 1024; ++i) {
    EXPECT_EQ(nullptr, strsep(&p, ":"));
  }

  // Some existing implementations have a separate return path for this.
  char non_empty_at_end[] = "hello:world";
  p = non_empty_at_end;
  EXPECT_STREQ("hello", strsep(&p, ":"));
  EXPECT_STREQ("world", strsep(&p, ":"));
  for (size_t i = 0; i < 1024; ++i) {
    EXPECT_EQ(nullptr, strsep(&p, ":"));
  }

  // Check that the implementation copes with top bit set characters.
  char top_bit_set_str[] = "hello\x80world";
  p = top_bit_set_str;
  EXPECT_STREQ("hello", strsep(&p, "\x80"));
  EXPECT_STREQ("world", strsep(&p, "\x80"));
  EXPECT_EQ(nullptr, strsep(&p, "\x80"));
}

TEST(STRING_TEST, strtok) {
  char empty[] = "";
  EXPECT_EQ(nullptr, strtok(empty, ":"));

  char only_delimiters[] = ":::";
  EXPECT_EQ(nullptr, strtok(only_delimiters, ":"));

  // Unlike strsep(), strtok() doesn't return empty strings.
  char str[] = ":hello:world:::foo:";
  EXPECT_STREQ("hello", strtok(str, ":"));
  EXPECT_STREQ("world", strtok(nullptr, ":"));
  EXPECT_STREQ("foo", strtok(nullptr, ":"));
  EXPECT_EQ(nullptr, strtok(nullptr, ":"));
  // Repeated calls after the first nullptr keep returning nullptr.
  for (size_t i = 0; i < 1024; ++i) {
    EXPECT_EQ(nullptr, strtok(nullptr, ":"));
  }

  // Some existing implementations have a separate return path for this.
  char non_empty_at_end[] = "hello:world";
  EXPECT_STREQ("hello", strtok(non_empty_at_end, ":"));
  EXPECT_STREQ("world", strtok(nullptr, ":"));
  for (size_t i = 0; i < 1024; ++i) {
    EXPECT_EQ(nullptr, strtok(nullptr, ":"));
  }

  // Check that the implementation copes with top bit set characters.
  char top_bit_set_str[] = "hello\x80world";
  EXPECT_STREQ("hello", strtok(top_bit_set_str, "\x80"));
  EXPECT_STREQ("world", strtok(nullptr, "\x80"));
  EXPECT_EQ(nullptr, strtok(nullptr, "\x80"));
}

TEST(STRING_TEST, strtok_r) {
  char* p;

  char empty[] = "";
  EXPECT_EQ(nullptr, strtok_r(empty, ":", &p));

  char only_delimiters[] = ":::";
  EXPECT_EQ(nullptr, strtok_r(only_delimiters, ":", &p));

  // Unlike strsep(), strtok_r() doesn't return empty strings.
  char str[] = ":hello:world:::foo:";
  EXPECT_STREQ("hello", strtok_r(str, ":", &p));
  EXPECT_STREQ("world", strtok_r(nullptr, ":", &p));
  EXPECT_STREQ("foo", strtok_r(nullptr, ":", &p));
  EXPECT_EQ(nullptr, strtok_r(nullptr, ":", &p));
  // Repeated calls after the first nullptr keep returning nullptr.
  for (size_t i = 0; i < 1024; ++i) {
    EXPECT_EQ(nullptr, p);
    EXPECT_EQ(nullptr, strtok_r(nullptr, ":", &p));
  }

  // Some existing implementations have a separate return path for this.
  char non_empty_at_end[] = "hello:world";
  EXPECT_STREQ("hello", strtok_r(non_empty_at_end, ":", &p));
  EXPECT_STREQ("world", strtok_r(nullptr, ":", &p));
  for (size_t i = 0; i < 1024; ++i) {
    EXPECT_EQ(nullptr, p);
    EXPECT_EQ(nullptr, strtok_r(nullptr, ":", &p));
  }

  // Check that the implementation copes with top bit set characters.
  char top_bit_set_str[] = "hello\x80world";
  EXPECT_STREQ("hello", strtok_r(top_bit_set_str, "\x80", &p));
  EXPECT_STREQ("world", strtok_r(nullptr, "\x80", &p));
  EXPECT_EQ(nullptr, strtok_r(nullptr, "\x80", &p));
}

Messung V0.5 in Prozent
C=91 H=84 G=87

¤ Dauer der Verarbeitung: 0.64 Sekunden  (vorverarbeitet am  2026-06-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.