Make env_var_t store its values via shared_ptr

This switches env_var_t to be an immutable value type, and stores its
contents via a shared_ptr. This eliminates string copying when fetching
env_var_t values.
This commit is contained in:
ridiculousfish 2019-03-25 00:41:04 -07:00
parent 93d70fae11
commit ea3a368c50
2 changed files with 52 additions and 27 deletions

View file

@ -1239,17 +1239,16 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo
has_changed_new = true;
}
var.set_vals(std::move(val));
var.set_pathvar(var_mode & ENV_PATHVAR);
var.set_read_only(is_read_only(key));
var = var.setting_vals(std::move(val))
.setting_exports(var_mode & ENV_EXPORT)
.setting_pathvar(var_mode & ENV_PATHVAR)
.setting_read_only(is_read_only(key));
if (var_mode & ENV_EXPORT) {
// The new variable is exported.
var.set_exports(true);
node->exportv = true;
has_changed_new = true;
} else {
var.set_exports(false);
// Set the node's exported when it changes something about exports
// (also when it redefines a variable to not be exported).
node->exportv = has_changed_old != has_changed_new;
@ -1351,20 +1350,16 @@ int env_stack_t::remove(const wcstring &key, int var_mode) {
return erased ? ENV_OK : ENV_NOT_FOUND;
}
const wcstring_list_t &env_var_t::as_list() const { return vals; }
const wcstring_list_t &env_var_t::as_list() const { return *vals_; }
wchar_t env_var_t::get_delimiter() const {
return is_pathvar() ? PATH_ARRAY_SEP : NONPATH_ARRAY_SEP;
}
/// Return a string representation of the var.
wcstring env_var_t::as_string() const {
return join_strings(vals, get_delimiter());
}
wcstring env_var_t::as_string() const { return join_strings(*vals_, get_delimiter()); }
void env_var_t::to_list(wcstring_list_t &out) const {
out = vals;
}
void env_var_t::to_list(wcstring_list_t &out) const { out = *vals_; }
env_var_t::env_var_flags_t env_var_t::flags_for(const wchar_t *name) {
env_var_flags_t result = 0;
@ -1372,6 +1367,12 @@ env_var_t::env_var_flags_t env_var_t::flags_for(const wchar_t *name) {
return result;
}
/// \return a singleton empty list, to avoid unnecessary allocations in env_var_t.
std::shared_ptr<const wcstring_list_t> env_var_t::empty_list() {
static const auto result = std::make_shared<const wcstring_list_t>();
return result;
}
maybe_t<env_var_t> env_stack_t::get(const wcstring &key, env_mode_flags_t mode) const {
const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL);
const bool search_local = !has_scope || (mode & ENV_LOCAL);

View file

@ -66,13 +66,22 @@ void env_init(const struct config_paths_t *paths = NULL);
/// routines.
void misc_init();
/// env_var_t is an immutable value-type data structure representing the value of an environment
/// variable.
class env_var_t {
public:
using env_var_flags_t = uint8_t;
private:
wcstring_list_t vals; // list of values assigned to the var
env_var_flags_t flags;
env_var_t(std::shared_ptr<const wcstring_list_t> vals, env_var_flags_t flags)
: vals_(std::move(vals)), flags_(flags) {}
/// The list of values in this variable.
/// shared_ptr allows for cheap copying.
std::shared_ptr<const wcstring_list_t> vals_{empty_list()};
/// Flag in this variable.
env_var_flags_t flags_{};
public:
enum {
@ -82,24 +91,27 @@ class env_var_t {
};
// Constructors.
env_var_t() = default;
env_var_t(const env_var_t &) = default;
env_var_t(env_var_t &&) = default;
env_var_t(wcstring_list_t vals, env_var_flags_t flags) : vals(std::move(vals)), flags(flags) {}
env_var_t(wcstring_list_t vals, env_var_flags_t flags)
: env_var_t(std::make_shared<wcstring_list_t>(std::move(vals)), flags) {}
env_var_t(wcstring val, env_var_flags_t flags)
: env_var_t(wcstring_list_t{std::move(val)}, flags) {}
// Constructors that infer the flags from a name.
env_var_t(const wchar_t *name, wcstring_list_t vals)
: env_var_t(std::move(vals), flags_for(name)) {}
env_var_t(const wchar_t *name, wcstring val) : env_var_t(std::move(val), flags_for(name)) {}
env_var_t() = default;
bool empty() const { return vals.empty() || (vals.size() == 1 && vals[0].empty()); }
bool read_only() const { return flags & flag_read_only; }
bool exports() const { return flags & flag_export; }
bool is_pathvar() const { return flags & flag_pathvar; }
env_var_flags_t get_flags() const { return flags; }
bool empty() const { return vals_->empty() || (vals_->size() == 1 && vals_->front().empty()); }
bool read_only() const { return flags_ & flag_read_only; }
bool exports() const { return flags_ & flag_export; }
bool is_pathvar() const { return flags_ & flag_pathvar; }
env_var_flags_t get_flags() const { return flags_; }
wcstring as_string() const;
void to_list(wcstring_list_t &out) const;
@ -108,38 +120,50 @@ class env_var_t {
/// \return the character used when delimiting quoted expansion.
wchar_t get_delimiter() const;
void set_vals(wcstring_list_t v) { vals = std::move(v); }
/// \return a copy of this variable with new values.
env_var_t setting_vals(wcstring_list_t vals) const {
return env_var_t{std::move(vals), flags_};
}
void set_exports(bool exportv) {
env_var_t setting_exports(bool exportv) const {
env_var_flags_t flags = flags_;
if (exportv) {
flags |= flag_export;
} else {
flags &= ~flag_export;
}
return env_var_t{vals_, flags};
}
void set_pathvar(bool pathvar) {
env_var_t setting_pathvar(bool pathvar) const {
env_var_flags_t flags = flags_;
if (pathvar) {
flags |= flag_pathvar;
} else {
flags &= ~flag_pathvar;
}
return env_var_t{vals_, flags};
}
void set_read_only(bool read_only) {
env_var_t setting_read_only(bool read_only) const {
env_var_flags_t flags = flags_;
if (read_only) {
flags |= flag_read_only;
} else {
flags &= ~flag_read_only;
}
return env_var_t{vals_, flags};
}
static env_var_flags_t flags_for(const wchar_t *name);
static std::shared_ptr<const wcstring_list_t> empty_list();
env_var_t &operator=(const env_var_t &var) = default;
env_var_t &operator=(env_var_t &&) = default;
bool operator==(const env_var_t &rhs) const { return vals == rhs.vals && flags == rhs.flags; }
bool operator==(const env_var_t &rhs) const {
return *vals_ == *rhs.vals_ && flags_ == rhs.flags_;
}
bool operator!=(const env_var_t &rhs) const { return ! (*this == rhs); }
};