uint32_t bufferAction(Context& ctxt, char insert_value, uint32_t max_alloc, BufferList& buffers,
                      const test::common::buffer::Action& action) {
  const uint32_t target_index = action.target_index() % BufferCount;
  Buffer::Instance& target_buffer = *buffers[target_index];
  uint32_t allocated = 0;

  switch (action.action_selector_case()) {
  case test::common::buffer::Action::kAddBufferFragment: {
    const uint32_t size = clampSize(action.add_buffer_fragment(), max_alloc);
    allocated += size;
    void* p = ::malloc(size);
    FUZZ_ASSERT(p != nullptr);
    ::memset(p, insert_value, size);
    auto fragment =
        std::make_unique<Buffer::BufferFragmentImpl>(p, size, releaseFragmentAllocation);
    ctxt.fragments_.emplace_back(std::move(fragment));
    target_buffer.addBufferFragment(*ctxt.fragments_.back());
    break;
  }
  case test::common::buffer::Action::kAddString: {
    const uint32_t size = clampSize(action.add_string(), max_alloc);
    allocated += size;
    const std::string data(size, insert_value);
    target_buffer.add(data);
    break;
  }
  case test::common::buffer::Action::kAddBuffer: {
    const uint32_t source_index = action.add_buffer() % BufferCount;
    if (target_index == source_index) {
      break;
    }
    Buffer::Instance& source_buffer = *buffers[source_index];
    if (source_buffer.length() > max_alloc) {
      break;
    }
    allocated += source_buffer.length();
    target_buffer.add(source_buffer);
    break;
  }
  case test::common::buffer::Action::kPrependString: {
    const uint32_t size = clampSize(action.prepend_string(), max_alloc);
    allocated += size;
    const std::string data(size, insert_value);
    target_buffer.prepend(data);
    break;
  }
  case test::common::buffer::Action::kPrependBuffer: {
    const uint32_t source_index = action.prepend_buffer() % BufferCount;
    if (target_index == source_index) {
      break;
    }
    Buffer::Instance& source_buffer = *buffers[source_index];
    if (source_buffer.length() > max_alloc) {
      break;
    }
    allocated += source_buffer.length();
    target_buffer.prepend(source_buffer);
    break;
  }
  case test::common::buffer::Action::kReserveCommit: {
    const uint32_t reserve_length = clampSize(action.reserve_commit().reserve_length(), max_alloc);
    allocated += reserve_length;
    if (reserve_length == 0) {
      break;
    }
    constexpr uint32_t reserve_slices = 16;
    Buffer::RawSlice slices[reserve_slices];
    const uint32_t allocated_slices = target_buffer.reserve(reserve_length, slices, reserve_slices);
    uint32_t allocated_length = 0;
    for (uint32_t i = 0; i < allocated_slices; ++i) {
      ::memset(slices[i].mem_, insert_value, slices[i].len_);
      allocated_length += slices[i].len_;
    }
    FUZZ_ASSERT(reserve_length <= allocated_length);
    const uint32_t target_length =
        std::min(reserve_length, action.reserve_commit().commit_length());
    uint32_t shrink_length = allocated_length;
    int32_t shrink_slice = allocated_slices - 1;
    while (shrink_length > target_length) {
      FUZZ_ASSERT(shrink_slice >= 0);
      const uint32_t available = slices[shrink_slice].len_;
      const uint32_t remainder = shrink_length - target_length;
      if (available >= remainder) {
        slices[shrink_slice].len_ -= remainder;
        break;
      }
      shrink_length -= available;
      slices[shrink_slice--].len_ = 0;
    }
    target_buffer.commit(slices, allocated_slices);
    break;
  }
  case test::common::buffer::Action::kCopyOut: {
    const uint32_t start =
        std::min(action.copy_out().start(), static_cast<uint32_t>(target_buffer.length()));
    const uint32_t length =
        std::min(static_cast<uint32_t>(target_buffer.length() - start), action.copy_out().length());
    // Make this static to avoid potential continuous ASAN inspired allocation.
    static uint8_t copy_buffer[TotalMaxAllocation];
    target_buffer.copyOut(start, length, copy_buffer);
    const std::string data = target_buffer.toString();
    FUZZ_ASSERT(::memcmp(copy_buffer, data.data() + start, length) == 0);
    break;
  }
  case test::common::buffer::Action::kDrain: {
    const uint32_t previous_length = target_buffer.length();
    const uint32_t drain_length =
        std::min(static_cast<uint32_t>(target_buffer.length()), action.drain());
    target_buffer.drain(drain_length);
    FUZZ_ASSERT(previous_length - drain_length == target_buffer.length());
    break;
  }
  case test::common::buffer::Action::kLinearize: {
    const uint32_t linearize_size =
        std::min(static_cast<uint32_t>(target_buffer.length()), action.linearize());
    target_buffer.linearize(linearize_size);
    break;
  }
  case test::common::buffer::Action::kMove: {
    const uint32_t source_index = action.move().source_index() % BufferCount;
    if (target_index == source_index) {
      break;
    }
    Buffer::Instance& source_buffer = *buffers[source_index];
    if (action.move().length() == 0) {
      if (source_buffer.length() > max_alloc) {
        break;
      }
      allocated += source_buffer.length();
      target_buffer.move(source_buffer);
    } else {
      const uint32_t source_length =
          std::min(static_cast<uint32_t>(source_buffer.length()), action.move().length());
      const uint32_t move_length = clampSize(max_alloc, source_length);
      if (move_length == 0) {
        break;
      }
      target_buffer.move(source_buffer, move_length);
      allocated += move_length;
    }
    break;
  }
  case test::common::buffer::Action::kRead: {
    const uint32_t max_length = clampSize(action.read(), max_alloc);
    allocated += max_length;
    if (max_length == 0) {
      break;
    }
    int pipe_fds[2] = {0, 0};
    FUZZ_ASSERT(::pipe(pipe_fds) == 0);
    Network::IoSocketHandleImpl io_handle(pipe_fds[0]);
    FUZZ_ASSERT(::fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == 0);
    FUZZ_ASSERT(::fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == 0);
    std::string data(max_length, insert_value);
    const ssize_t rc = ::write(pipe_fds[1], data.data(), max_length);
    FUZZ_ASSERT(rc > 0);
    Api::IoCallUint64Result result = target_buffer.read(io_handle, max_length);
    FUZZ_ASSERT(result.rc_ == static_cast<uint64_t>(rc));
    FUZZ_ASSERT(::close(pipe_fds[1]) == 0);
    break;
  }
  case test::common::buffer::Action::kWrite: {
    int pipe_fds[2] = {0, 0};
    FUZZ_ASSERT(::pipe(pipe_fds) == 0);
    Network::IoSocketHandleImpl io_handle(pipe_fds[1]);
    FUZZ_ASSERT(::fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == 0);
    FUZZ_ASSERT(::fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == 0);
    uint64_t rc;
    do {
      const bool empty = target_buffer.length() == 0;
      const std::string previous_data = target_buffer.toString();
      const auto result = target_buffer.write(io_handle);
      FUZZ_ASSERT(result.ok());
      rc = result.rc_;
      ENVOY_LOG_MISC(trace, "Write rc: {} errno: {}", rc,
                     result.err_ != nullptr ? result.err_->getErrorDetails() : "-");
      if (empty) {
        FUZZ_ASSERT(rc == 0);
      } else {
        auto buf = std::make_unique<char[]>(rc);
        FUZZ_ASSERT(static_cast<uint64_t>(::read(pipe_fds[0], buf.get(), rc)) == rc);
        FUZZ_ASSERT(::memcmp(buf.get(), previous_data.data(), rc) == 0);
      }
    } while (rc > 0);
    FUZZ_ASSERT(::close(pipe_fds[0]) == 0);
    break;
  }
  case test::common::buffer::Action::kGetRawSlices: {
    const uint64_t slices_needed = target_buffer.getRawSlices().size();
    const uint64_t slices_tested =
        std::min(slices_needed, static_cast<uint64_t>(action.get_raw_slices()));
    if (slices_tested == 0) {
      break;
    }
    Buffer::RawSliceVector raw_slices = target_buffer.getRawSlices(/*max_slices=*/slices_tested);
    const uint64_t slices_obtained = raw_slices.size();
    FUZZ_ASSERT(slices_obtained <= slices_needed);
    uint64_t offset = 0;
    const std::string data = target_buffer.toString();
    for (const auto& raw_slices : raw_slices) {
      FUZZ_ASSERT(::memcmp(raw_slices.mem_, data.data() + offset, raw_slices.len_) == 0);
      offset += raw_slices.len_;
    }
    FUZZ_ASSERT(slices_needed != slices_tested || offset == target_buffer.length());
    break;
  }
  case test::common::buffer::Action::kSearch: {
    const std::string& content = action.search().content();
    const uint32_t offset = action.search().offset();
    const std::string data = target_buffer.toString();
    FUZZ_ASSERT(target_buffer.search(content.data(), content.size(), offset) ==
                static_cast<ssize_t>(target_buffer.toString().find(content, offset)));
    break;
  }
  case test::common::buffer::Action::kStartsWith: {
    const std::string data = target_buffer.toString();
    FUZZ_ASSERT(target_buffer.startsWith(action.starts_with()) ==
                (data.find(action.starts_with()) == 0));
    break;
  }
  default:
    // Maybe nothing is set?
    break;
  }

  return allocated;
}