Refactor Config from a struct to a class.

This should make it easier to add new options, and to add options that
are complex. For example, I want to modify the behavior of
record_allocs_file so that it also enables record_allocs to a default
state.

Test: All unit tests pass.
Test: Enable the backtrace option and restart.
Change-Id: Idf5cdeed06ade3bc2c8ae39d228734bf65209b4f
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index ca56dc8..d8a7069 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -32,6 +32,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <unordered_map>
 
 constexpr uint64_t FRONT_GUARD = 0x1;
 constexpr uint64_t REAR_GUARD = 0x2;
@@ -55,34 +56,100 @@
 // If one or more of these options is set, then a special header is needed.
 constexpr uint64_t HEADER_OPTIONS = FRONT_GUARD | REAR_GUARD | BACKTRACE | FREE_TRACK | LEAK_TRACK;
 
-struct Config {
-  bool Set(const char* str);
+class Config {
+ public:
+  bool Init(const char* options_str);
 
-  size_t front_guard_bytes = 0;
-  size_t rear_guard_bytes = 0;
+  void LogUsage() const;
 
-  bool backtrace_enable_on_signal = false;
-  int backtrace_signal = 0;
-  bool backtrace_enabled = false;
-  size_t backtrace_frames = 0;
+  uint64_t options() const { return options_; }
 
-  size_t fill_on_alloc_bytes = 0;
-  size_t fill_on_free_bytes = 0;
+  int backtrace_signal() const { return backtrace_signal_; }
+  size_t backtrace_frames() const { return backtrace_frames_; }
+  size_t backtrace_enabled() const { return backtrace_enabled_; }
+  size_t backtrace_enable_on_signal() const { return backtrace_enable_on_signal_; }
 
-  size_t expand_alloc_bytes = 0;
+  size_t front_guard_bytes() const { return front_guard_bytes_; }
+  size_t rear_guard_bytes() const { return rear_guard_bytes_; }
+  uint8_t front_guard_value() const { return front_guard_value_; }
+  uint8_t rear_guard_value() const { return rear_guard_value_; }
 
-  size_t free_track_allocations = 0;
-  size_t free_track_backtrace_num_frames = 0;
+  size_t expand_alloc_bytes() const { return expand_alloc_bytes_; }
 
-  int record_allocs_signal = 0;
-  size_t record_allocs_num_entries = 0;
-  std::string record_allocs_file;
+  size_t free_track_allocations() const { return free_track_allocations_; }
+  size_t free_track_backtrace_num_frames() const { return free_track_backtrace_num_frames_; }
 
-  uint64_t options = 0;
-  uint8_t fill_alloc_value;
-  uint8_t fill_free_value;
-  uint8_t front_guard_value;
-  uint8_t rear_guard_value;
+  size_t fill_on_alloc_bytes() const { return fill_on_alloc_bytes_; }
+  size_t fill_on_free_bytes() const { return fill_on_free_bytes_; }
+  uint8_t fill_alloc_value() const { return fill_alloc_value_; }
+  uint8_t fill_free_value() const { return fill_free_value_; }
+
+  int record_allocs_signal() const { return record_allocs_signal_; }
+  size_t record_allocs_num_entries() const { return record_allocs_num_entries_; }
+  const std::string& record_allocs_file() const { return record_allocs_file_; }
+
+ private:
+  struct OptionInfo {
+    uint64_t option;
+    bool (Config::*process_func)(const std::string&, const std::string&);
+  };
+
+  bool ParseValue(const std::string& option, const std::string& value, size_t default_value,
+                  size_t min_value, size_t max_value, size_t* new_value) const;
+
+  bool ParseValue(const std::string& option, const std::string& value, size_t min_value,
+                  size_t max_value, size_t* parsed_value) const;
+
+  bool SetGuard(const std::string& option, const std::string& value);
+  bool SetFrontGuard(const std::string& option, const std::string& value);
+  bool SetRearGuard(const std::string& option, const std::string& value);
+
+  bool SetFill(const std::string& option, const std::string& value);
+  bool SetFillOnAlloc(const std::string& option, const std::string& value);
+  bool SetFillOnFree(const std::string& option, const std::string& value);
+
+  bool SetBacktrace(const std::string& option, const std::string& value);
+  bool SetBacktraceEnableOnSignal(const std::string& option, const std::string& value);
+
+  bool SetExpandAlloc(const std::string& option, const std::string& value);
+
+  bool SetFreeTrack(const std::string& option, const std::string& value);
+  bool SetFreeTrackBacktraceNumFrames(const std::string& option, const std::string& value);
+
+  bool SetRecordAllocs(const std::string& option, const std::string& value);
+  bool SetRecordAllocsFile(const std::string& option, const std::string& value);
+
+  bool VerifyValueEmpty(const std::string& option, const std::string& value);
+
+  static bool GetOption(const char** option_str, std::string* option, std::string* value);
+
+  const static std::unordered_map<std::string, OptionInfo> kOptions;
+
+  size_t front_guard_bytes_ = 0;
+  size_t rear_guard_bytes_ = 0;
+
+  bool backtrace_enable_on_signal_ = false;
+  int backtrace_signal_ = 0;
+  bool backtrace_enabled_ = false;
+  size_t backtrace_frames_ = 0;
+
+  size_t fill_on_alloc_bytes_ = 0;
+  size_t fill_on_free_bytes_ = 0;
+
+  size_t expand_alloc_bytes_ = 0;
+
+  size_t free_track_allocations_ = 0;
+  size_t free_track_backtrace_num_frames_ = 0;
+
+  int record_allocs_signal_ = 0;
+  size_t record_allocs_num_entries_ = 0;
+  std::string record_allocs_file_;
+
+  uint64_t options_ = 0;
+  uint8_t fill_alloc_value_;
+  uint8_t fill_free_value_;
+  uint8_t front_guard_value_;
+  uint8_t rear_guard_value_;
 };
 
 #endif  // MALLOC_DEBUG_CONFIG_H