[libc-commits] [libc] [libc][stdlib] Add EnvironmentManager (PR #195260)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Tue May 5 07:35:52 PDT 2026
================
@@ -0,0 +1,179 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of internal environment management utilities.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/environ_internal.h"
+#include "config/app.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/alloc-checker.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+// Minimum initial capacity for the environment array when first allocated.
+// This avoids frequent reallocations for small environments.
+constexpr size_t MIN_ENVIRON_CAPACITY = 32;
+
+// Growth factor for environment array capacity when expanding.
+// When capacity is exceeded, new_capacity = old_capacity *
+// ENVIRON_GROWTH_FACTOR.
+constexpr size_t ENVIRON_GROWTH_FACTOR = 2;
+
+void EnvironmentManager::init_once() {
+ if (initialized)
+ return;
+
+ // Count entries in the startup environ.
+ char **env_ptr = reinterpret_cast<char **>(app.env_ptr);
+ if (env_ptr) {
+ size_t c = 0;
+ for (char **env = env_ptr; *env != nullptr; env++)
+ c++;
+ count = c;
+ }
+
+ initialized = true;
+}
+
+EnvironmentManager &EnvironmentManager::get_instance() {
+ static EnvironmentManager mgr;
+ mgr.init_once();
+ return mgr;
+}
+
+char **EnvironmentManager::get_array() {
+ if (is_ours)
+ return storage;
+ return reinterpret_cast<char **>(app.env_ptr);
+}
+
+EnvironmentManager::iterator EnvironmentManager::begin() { return get_array(); }
+
+EnvironmentManager::iterator EnvironmentManager::end() {
+ return get_array() + count;
+}
+
+size_t EnvironmentManager::size() const { return count; }
+
+char *EnvironmentManager::get(cpp::string_view name) {
+ cpp::optional<size_t> idx = find_var(name);
+ if (!idx)
+ return nullptr;
+ return get_array()[*idx] + name.size() + 1;
+}
+
+cpp::optional<size_t> EnvironmentManager::find_var(cpp::string_view name) {
+ char **env_array = get_array();
+ if (!env_array)
+ return cpp::nullopt;
+
+ for (size_t i = 0; i < count; i++) {
+ cpp::string_view current(env_array[i]);
----------------
kaladron wrote:
Thinking this through:
We need to make sure we don't accidentally read past the string, since that would be UB. Because of setenv and putenv, strings aren't contiguous so there's no pointer arithmetic possible to just calculate the length.
Three other possibilities came to mind:
1) We could use strnlen or something to cap the length at the size of the prefix. WE'd have to do +1 to be able to check that we weren't dealing with a substring.
2) We could drop the string_view and manually do a comparison between the two strings. We'd lose the efficiencies that strlen has (assuming it's not just an incrementing loop, but that's pretty unlikely.
3) We could internally use Pascal strings and store the length of them in the word ahead, but that's only really applicable to strings we've added unless we copied all the environment strings on startup.
My suspicion is that people aren't often calling getenv in the hot path (espeicially if on glibc it potentially includes locking) , but I'm happy to implement it if you'd like. Or I wonder if it would be worth creating a label for "potential optimisations" and we could file issues for people to hunt this sort of thing down.
https://github.com/llvm/llvm-project/pull/195260
More information about the libc-commits
mailing list