// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_METRICS_FIELD_TRIAL_ENTRY_H_
#define BASE_METRICS_FIELD_TRIAL_ENTRY_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <string>
#include <string_view>
#include <vector>

#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/pickle.h"

namespace base::internal {

// Used by field_trial.cc to represent field trials in a shared memory segment
// that's shared with child processes. One FieldTrialEntry is created per field
// trial and is followed by a base::Pickle object that gets decoded and read.
struct BASE_EXPORT FieldTrialEntry {
  // SHA1(FieldTrialEntry): Increment this if structure changes!
  static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 3;

  // Expected size for 32/64-bit check.
  static constexpr size_t kExpectedInstanceSize = 16;

  // Retrieves field trial state from an allocator so that it can be analyzed
  // after a crash. The pointers in the returned vector are into the persistent
  // memory segment and so are only valid as long as the allocator is valid.
  static std::vector<const FieldTrialEntry*>
  GetAllFieldTrialsFromPersistentAllocator(
      const PersistentMemoryAllocator& allocator);

  // Return a pointer to the data area immediately following the entry.
  uint8_t* GetPickledDataPtr() {
    return UNSAFE_TODO(reinterpret_cast<uint8_t*>(this + 1));
  }
  const uint8_t* GetPickledDataPtr() const {
    return UNSAFE_TODO(reinterpret_cast<const uint8_t*>(this + 1));
  }

  // Whether or not this field trial is activated. This is really just a
  // boolean but using a 32 bit value for portability reasons. It should be
  // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler
  // from doing unexpected optimizations because it thinks that only one
  // thread is accessing the memory location.
  subtle::Atomic32 activated;

  // On e.g. x86, alignof(uint64_t) is 4.  Ensure consistent size and
  // alignment of `pickle_size` across platforms. This can be considered
  // to be padding for the final 32 bit value (activated). If this struct
  // gains or loses fields, consider if this padding is still needed.
  uint32_t padding;

  // Size of the pickled structure, NOT the total size of this entry.
  uint64_t pickle_size;

  // Calling this is only valid when the entry is initialized. That is, it
  // resides in shared memory and has a pickle containing the trial name,
  // group name, and is_overridden.
  bool GetState(std::string_view& trial_name,
                std::string_view& group_name,
                bool& is_overridden) const;

  // Calling this is only valid when the entry is initialized as well. Reads
  // the parameters following the trial and group name and stores them as
  // key-value mappings in |params|.
  bool GetParams(std::map<std::string, std::string>* params) const;

 private:
  // Returns an iterator over the data containing names and params.
  PickleIterator GetPickleIterator() const;

  // Takes the iterator and writes out the first two items into |trial_name|
  // and |group_name|.
  bool ReadStringPair(PickleIterator* iter,
                      std::string_view* trial_name,
                      std::string_view* group_name) const;

  // Reads the field trial header, which includes the name of the trial and
  // group, and the is_overridden bool.
  bool ReadHeader(PickleIterator& iter,
                  std::string_view& trial_name,
                  std::string_view& group_name,
                  bool& is_overridden) const;
};

}  // namespace base::internal

#endif  // BASE_METRICS_FIELD_TRIAL_ENTRY_H_
