| 196 | } |
| 197 | |
| 198 | cbm_mem_budget_t cbm_mem_resolve_budget(size_t total_ram, double ram_fraction, |
| 199 | const char *budget_mb) { |
| 200 | if (ram_fraction <= 0.0 || ram_fraction > MAX_RAM_FRACTION) { |
| 201 | ram_fraction = DEFAULT_RAM_FRACTION; |
| 202 | } |
| 203 | cbm_mem_budget_t result = { |
| 204 | .budget = (size_t)((double)total_ram * ram_fraction), |
| 205 | .source = "ram_fraction", |
| 206 | .clamped = false, |
| 207 | .invalid = false, |
| 208 | .hard_capped = false, |
| 209 | }; |
| 210 | |
| 211 | if (budget_mb == NULL || budget_mb[0] == '\0') { |
| 212 | return result; /* no override → fraction-derived budget */ |
| 213 | } |
| 214 | |
| 215 | /* Strict parse, matching the src/foundation/limits.c convention: reject |
| 216 | * trailing garbage (`*end`), overflow (errno==ERANGE), and non-positive |
| 217 | * values. This turns a fat-fingered value (e.g. "8GB", or a 20-digit typo) |
| 218 | * into a clean fallback-with-warning rather than a silently wrong budget. */ |
| 219 | errno = 0; |
| 220 | char *end = NULL; |
| 221 | long long want_mb = strtoll(budget_mb, &end, CBM_DECIMAL_BASE); |
| 222 | if (errno != 0 || end == budget_mb || *end != '\0' || want_mb <= 0) { |
| 223 | result.invalid = true; /* keep the fraction-derived budget */ |
| 224 | return result; |
| 225 | } |
| 226 | |
| 227 | result.source = "CBM_MEM_BUDGET_MB"; |
| 228 | size_t want = (size_t)want_mb; |
| 229 | if (total_ram > 0) { |
| 230 | /* Compare in MiB space so a valid-but-huge request (e.g. 2^44 MiB, which |
| 231 | * would overflow the ×MiB byte multiply) clamps cleanly to total_ram. */ |
| 232 | if (want > total_ram / MB_DIVISOR) { |
| 233 | result.budget = total_ram; |
| 234 | result.clamped = true; |
| 235 | } else { |
| 236 | result.budget = want * MB_DIVISOR; |
| 237 | } |
| 238 | } else if (want > SIZE_MAX / MB_DIVISOR) { |
| 239 | /* RAM detection failed (no clamp target) and the request is |
| 240 | * astronomically large — cap at SIZE_MAX rather than wrap. */ |
| 241 | result.budget = SIZE_MAX; |
| 242 | } else { |
| 243 | result.budget = want * MB_DIVISOR; |
| 244 | } |
| 245 | return result; |
| 246 | } |
| 247 | |
| 248 | cbm_mem_budget_t cbm_mem_resolve_budget_capped(size_t total_ram, double ram_fraction, |
| 249 | const char *budget_mb, size_t hard_cap_bytes) { |
no outgoing calls
no test coverage detected