Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ce31629
chore(stlsoft): update for 1.11.1-rc9 release
synesissoftware Oct 10, 2026
e30a2af
test(stlsoft): unit-test resizeable_buffer_resize helper
synesissoftware Oct 15, 2026
498a6bb
TODO.md
synesissoftware Oct 15, 2026
e7cd3e7
docs(stlsoft): capture Resizeable Buffer concept in CONCEPTS.md
synesissoftware Oct 15, 2026
ac0c13d
fix(unixstl): harden home/env resizeable-buffer overloads and cover them
synesissoftware Oct 15, 2026
9c8378f
test(stlsoft): cover filesystem_traits path rb overloads
synesissoftware Oct 15, 2026
5906475
fix(stlsoft): align filesystem_traits ensure/remove_dir_end rb (C2/C3)
synesissoftware Oct 15, 2026
3566ab1
docs(stlsoft): add Resizeable Buffer concept example
synesissoftware Oct 15, 2026
dbb7654
tidy
synesissoftware Oct 15, 2026
eb4cc36
fix(stlsoft): set OOM last-error on filesystem_traits resize failures…
synesissoftware Oct 15, 2026
23af7a5
docs(stlsoft): enrich resizeable_buffer_helpers Doxygen (D2)
synesissoftware Oct 15, 2026
bb14877
docs(stlsoft): call out Resizeable Buffer in FAQ, CHANGES, NEWS
synesissoftware Oct 15, 2026
f2412e8
fix(stlsoft): align unixstl remove_dir_end strip-all; fix readdir sof…
synesissoftware Oct 15, 2026
4c234bb
fix(stlsoft): size full-path scratch to 1+len (G6 OOB)
synesissoftware Oct 15, 2026
4ec4d80
fix(stlsoft): support defaulted-arg resize in resizeable_buffer_resiz…
synesissoftware Oct 15, 2026
4f7d0d5
date
synesissoftware Oct 17, 2026
0d5000c
Documentation, commenting, canonicalisation (#57)
synesissoftware Oct 17, 2026
d35f5fb
Preconditions (#56)
synesissoftware Oct 17, 2026
1620b64
merge fix: removing vestigial duplicate
synesissoftware Oct 17, 2026
f54dbf3
Merge branch 'dev' into 'resizeable_buffer'
synesissoftware Oct 17, 2026
23e6b47
Merge branch 'dev' into 'resizeable_buffer'
synesissoftware Oct 19, 2026
f14a682
feat(winstl): add get_module_directory resizeable-buffer overload (G2)
synesissoftware Oct 19, 2026
74d843f
TODO.md
synesissoftware Oct 19, 2026
03b7ada
fix(stlsoft): silence ensure_dir_end GCC bounds; fix module-dir GT args
synesissoftware Oct 19, 2026
41c552b
test(stlsoft): avoid tiny-stack empty ensure_dir_end under GCC -Werror
synesissoftware Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
# STLSoft - CHANGES <!-- omit in toc -->


## 1.11.1-rc10 - 20th October 2026

* additions:
* Added **CONCEPTS.md** — documents the **Resizeable Buffer** concept (requirements, models / non-models, probe-then-fill gold contract for traits `rb` overloads);
* Added example **examples/by_concept/resizeable_buffer** — probe-then-fill into `auto_buffer` / `vector` via `platformstl` traits `rb` overloads and `resizeable_buffer_resize`;
* **winstl::system_traits** — added `get_module_directory(hModule, rb)` (A+W), gold probe-then-fill companion to the raw overload and to `get_module_filename(rb)`;
* fixes:
* **unixstl::system_traits** — `get_home_directory(rb)` and `get_environment_variable(rb)` follow the gold pattern (early-out on probe `0`, conditional resize, `ENOMEM` on failure); environment `rb` demand includes the trailing NUL (`cchValue + 1`);
* **unixstl** / **winstl** `filesystem_traits` — `get_full_path_name` / `get_short_path_name` / `get_current_directory` `rb` overloads no longer resize-then-`&rb[0]` on an empty buffer when the probe returns `0`; resize failure sets `ENOMEM` / `ERROR_OUTOFMEMORY`;
* **unixstl::filesystem_traits** — `ensure_dir_end(rb)` leaves a lone-NUL `""` unchanged (`n > 1`, matching WinSTL and the C-string overload);
* **winstl::filesystem_traits** — `remove_dir_end(rb)` shrinks `size()` to the post-strip length so a later `ensure_dir_end(rb)` cannot write past the NUL;
* **unixstl::filesystem_traits** — `remove_dir_end` (C-string and `rb`, A+W) now strips **all** trailing path separators, matching WinSTL (was strip-one);
* **unixstl** / **winstl** `filesystem_traits` — every `resizeable_buffer_resize` failure on the `rb` / scratch paths above reports last-error (including WinSTL `ensure_dir_end(rb)` and the UnixSTL full-path cwd scratch grow);
* **unixstl::readdir_sequence** — non-exception `absolutePath` failure path no longer contains a broken `\'0'` write; soft-fails with an empty string (parallel to the throw when exceptions are enabled);
* **unixstl** / **winstl** `filesystem_traits` — `get_full_path_name_impl` scratch for a non-NUL-terminated counted slice is sized `1 + len` (was `1 + (len - 1)`), so writing the trailing NUL is in-bounds; WinSTL quoted-path handling takes this branch;
* **stlsoft::resizeable_buffer_resize** — invoke `rb.resize(newSize)` directly (with SFINAE detection of one-argument `bool` / `ss_bool_t` `resize`) so pre-C++11-style `resize(size, value = value())` models work; VC6 path adds a two-argument `void` member-pointer overload;
* docs:
* Enriched Doxygen on **stlsoft/util/resizeable_buffer_helpers.hpp** (`resizeable_buffer_resize` parameters, return values, failure policy, caller OOM duty);
* **FAQ.md** — Q6 on the Resizeable Buffer concept;
* **EXAMPLES.md** — catalogues **by_concept/resizeable_buffer**;
* testing:
* Component tests for unixstl `system_traits` home / environment `rb` overloads;
* Component tests for unixstl / winstl filesystem path `rb` overloads;
* Unit tests for unixstl / winstl `ensure_dir_end` / `remove_dir_end` (including size / round-trip, failing-resize OOM reporting, and unixstl strip-all trailing separators);
* Unit tests for `stlsoft::resizeable_buffer_resize` (including a stub with defaulted fill-argument `resize`, G5);
* Unit tests for **winstl** `get_module_directory(rb)` (`auto_buffer` / `vector`, invalid handle, grow-from-empty, pre-sized buffer);


## 1.11.1-rc9 - 18th October 2026

* additions:
Expand Down
196 changes: 196 additions & 0 deletions CONCEPTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# STLSoft - Concepts <!-- omit in toc -->


## Table of Contents <!-- omit in toc -->

- [Introduction](#introduction)
- [Classic buffer-and-length OS APIs](#classic-buffer-and-length-os-apis)
- [What goes wrong](#what-goes-wrong)
- [Gold standard: probe, then fill](#gold-standard-probe-then-fill)
- [The Resizeable Buffer concept](#the-resizeable-buffer-concept)
- [Motivation](#motivation)
- [Requirements](#requirements)
- [Models and non-models](#models-and-non-models)
- [Helper: `resizeable_buffer_resize`](#helper-resizeable_buffer_resize)
- [Contract for traits overloads that take an `rb`](#contract-for-traits-overloads-that-take-an-rb)
- [See also](#see-also)


## Introduction

This document records **concepts** used across **STLSoft** — structural
requirements that are not (necessarily) reified as language-level concepts
or base classes, but that several APIs assume. The first entry is the
**Resizeable Buffer** concept used by **UnixSTL** / **WinSTL**
`system_traits` and `filesystem_traits` overloads that accept a generic
buffer rather than a raw pointer and length.


## Classic buffer-and-length OS APIs

A large class of operating-system and C library functions take a
**destination buffer** and a **capacity** (in characters or bytes), and
return either a written length or a failure code. Familiar examples
include `getcwd` / `_getcwd`, `GetCurrentDirectory`,
`GetEnvironmentVariable`, `ExpandEnvironmentStrings`, and
`GetModuleFileName`.

Callers typically need either:

* to **fill** a buffer they already own; or
* to **discover** how large a buffer is required before allocating;


### What goes wrong

Historical APIs do not share one contract. Common failure modes include:

* **Fixed ceilings** — e.g. relying solely on `_MAX_PATH` / `PATH_MAX`,
which is wrong for long paths and for systems without a compile-time
maximum;
* **Truncation without a true demand** — e.g. raw Windows'
[`GetModuleFileName`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew)
may truncate into the supplied buffer and return a value equal to the
capacity, without reporting the size that would have been sufficient
(callers then grow-and-retry, or wrap the API);
* **Ambiguous “empty destination”** — passing a **non-null** buffer with
**capacity 0** is *not* a reliable size probe on many APIs: it often
means “write nothing into this empty buffer” and returns `0` or fails,
rather than returning the required length;
* **Inconsistent NUL character accounting** — some returns count the
terminating `NUL`, some do not, and some change meaning between the
“too small” and “success” cases;


### Gold standard: probe, then fill

The normative, unambiguous pattern preferred in **STLSoft** facades is a
**two-phase** protocol:

1. **Probe** — call with a **null** buffer and a **zero** capacity
(`buff == nullptr` and `cch == 0`). The function returns a **demand**:
a character count of *at least* the full result length **plus one** for
the terminating `NUL` (it may return more than that minimum);
2. **Allocate** — obtain storage of at least that demand (stack, heap,
`auto_buffer`, `vector`, …);
3. **Fill** — call again with a non-null buffer and that capacity. On
success the return is the **length of the string excluding the
`NUL`**;

Preconditions on the raw overloads take the form:

```text
0 == cch || nullptr != buff
```

so that a zero capacity is allowed **only** when the buffer pointer is
null (the probe), and a non-null buffer always has a positive capacity
for a fill.

**Do not** probe by passing a non-null buffer with length zero. That
conflates “empty destination” with “size query” and is not portable
across OS APIs. Prefer **null buffer + zero length** for the probe;
wrappers that must paper over defective native APIs (such as
`GetModuleFileName`) should still *present* this gold contract to their
callers.

This probe-vs-fill convention is the gold standard summarised in the
discussion of required-length probing
([Ask Brave — probe with 0 length vs null buffer](https://search.brave.com/ask?q=What+about+when+wanting+to+probe+the+actual+required+length.+Should+the+caller+pass+0+length%2C+or+NUL...&source=quick-answer-followup&conversation=09aef0cd1c6f4bfdcbbb5fc05969c1dbd832)).


## The Resizeable Buffer concept

### Motivation

Repeating probe / allocate / fill at every call site is verbose and
easy to get wrong (especially NUL accounting and failure paths).
**STLSoft** therefore offers overloads that take a **resizeable
buffer** — any contiguous, resizable character sequence that can grow
to the demanded size — and perform the two-phase protocol internally.


### Requirements

A type `T` models **Resizeable Buffer** for an element type `CharT` when
it provides at least:

| Member | Meaning |
| --- | --- |
| `value_type` | Element type (typically `char` or `wchar_t`); |
| `size_type` | Unsigned size type; |
| `size() const` | Number of elements currently managed **including** the trailing `NUL` when the buffer is used as a C-string; |
| `empty() const` | Equivalent to `0 == size()`; |
| `operator[](size_type)` | Mutable access; contiguous storage so that `&rb[0]` addresses the first element when `size() > 0`; |
| `operator[](size_type) const` | Const access (needed by read-only helpers such as `has_dir_end`); |
| `resize(size_type)` | Change the number of managed elements. May return `bool` (`true` on success) or `void` (failure via exception or as documented for that type); |

**Invariants** assumed by path-oriented APIs:

* when `!empty()`, `rb[size() - 1] == CharT(0)` (trailing `NUL`);
* `resize` **preserves** existing elements up to the new size when
shrinking or growing (so helpers such as `ensure_dir_end` may append
in place);
* storage is **contiguous**;


### Models and non-models

| Type | Models? | Notes |
| --- | --- | --- |
| `stlsoft::auto_buffer<CharT>` | ✅ | Preferred; `resize` returns `bool`; |
| `std::vector<CharT>` | ✅ | `resize` returns `void`; use `resizeable_buffer_resize`; |
| `stlsoft::basic_path_buffer<…>` | ❌ | `size()` **excludes** the `NUL`; off-by-one if passed as an `rb`; |
| `winstl::basic_file_path_buffer<…>` | ❌ | `resize` is a no-op in some specialisations; |


### Helper: `resizeable_buffer_resize`

Declared in
[`stlsoft/util/resizeable_buffer_helpers.hpp`](./include/stlsoft/util/resizeable_buffer_helpers.hpp)
(Doxygen on the file and on the function documents parameters, return
values, failure policy, and the expectation that callers set
`ENOMEM` / `ERROR_OUTOFMEMORY` themselves).

`stlsoft::resizeable_buffer_resize(rb, newSize)` invokes `rb.resize(newSize)`
whether that member returns `bool` or `void`, and whether `resize` is a
one-argument member or a two-argument member with a defaulted fill (as on
pre-C++11 `std::vector`), and always returns `bool` (`true` on success).
Prefer it over calling `rb.resize` directly in generic code.


### Contract for traits overloads that take an `rb`

Overloads of the form `get_*(…, T_resizeableBuffer& rb)` (and similar)
should follow this gold sequence:

1. **Probe** via the raw overload with a null buffer and zero capacity;
2. If the probe returns `0` (failure) — **leave `rb` unchanged**, do
**not** take `&rb[0]`, return `0`;
3. If `rb.size() < cchRequired` — call `resizeable_buffer_resize(rb,
cchRequired)`; on failure set an appropriate out-of-memory status
(`ENOMEM` / `ERROR_OUTOFMEMORY`) and return `0` without taking
`&rb[0]` on an empty buffer;
4. **Fill** via the raw overload into `&rb[0]` with capacity `rb.size()`;
5. Optional out-parameters that point into the result (e.g. a basename
separator pointer) are computed **only after** the final fill — never
across a resize;

On success, the return value matches the raw fill overload (typically
the string length **excluding** the `NUL`). The buffer’s `size()` remains
the allocated demand (typically at least length + 1).


## See also

* Example:
[`examples/by_concept/resizeable_buffer/`](./examples/by_concept/resizeable_buffer/main.cpp);
* Pseudo-struct documentation in
`unixstl::filesystem_traits` / `winstl::filesystem_traits`
(`struct resizeable_buffer` under `\name concepts`);
* Unit tests:
[`test/unit/stlsoft/util/resizeable_buffer_helpers/`](./test/unit/stlsoft/util/resizeable_buffer_helpers/);
* [FAQ.md](./FAQ.md), [INSTALL.md](./INSTALL.md), [EXAMPLES.md](./EXAMPLES.md);


<!-- ########################### end of file ########################### -->
2 changes: 1 addition & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PROJECT_NAME = "STLSoft"
PROJECT_BRIEF = "Robust, lightweight, cross-platform, template software"
PROJECT_NUMBER = 1.11.1-rc9
PROJECT_NUMBER = 1.11.1-rc10

# Overridden at generation time by generate_doxygen.sh (respects
# SIS_CMAKE_BUILD_DIR).
Expand Down
1 change: 1 addition & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
| Name | Source & Description | Platform | Summary |
| ---- | -------------------- | -------- | ------- |
| **By concept**: | | | |
| **resizeable_buffer** | [by_concept/resizeable_buffer](./examples/by_concept/resizeable_buffer/main.cpp) | * | Probe-then-fill into `auto_buffer` / `vector` via traits `rb` overloads ([CONCEPTS.md](./CONCEPTS.md)) |
| **time_functions** | [by_concept/string_access_shims/time_functions](./examples/by_concept/string_access_shims/time_functions/main.cpp) | * | Format time-related types via string-access shims (e.g. `struct tm`, `std::chrono`) |
| | | | |
| **By project**: | | | |
Expand Down
19 changes: 19 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ it will be used to create one.
- [Q3: "How do I use STLSoft?"](#q3-how-do-i-use-stlsoft)
- [Q4: "Does STLSoft have unit-tests?"](#q4-does-stlsoft-have-unit-tests)
- [Q5: "Is STLSoft still relevant given the widespread support for 'Modern C++'?"](#q5-is-stlsoft-still-relevant-given-the-widespread-support-for-modern-c)
- [Q6: "What is a Resizeable Buffer?"](#q6-what-is-a-resizeable-buffer)


# FAQs: <!-- omit in toc -->
Expand Down Expand Up @@ -65,4 +66,22 @@ This is a good question. The Synesis open-source libraries — including
and more — continue to be maintained in light of contemporary C++ practice, while still supporting a wide range of compilers and platforms. That work informs the future of **STLSoft**, which may mean further investment in the existing 1.x stream or, eventually, a 2.x stream less burdened by historical compiler-feature detection.


## Q6: "What is a Resizeable Buffer?"

A **Resizeable Buffer** is a contiguous, growable character sequence (for
example `stlsoft::auto_buffer` or `std::vector`) that UnixSTL / WinSTL
`system_traits` and `filesystem_traits` overloads accept as a generic
`rb` instead of a raw pointer and length. Those overloads follow a
**probe-then-fill** contract: query demand with a null buffer and zero
capacity, resize only when needed (via
`stlsoft::resizeable_buffer_resize`), set `ENOMEM` /
`ERROR_OUTOFMEMORY` on resize failure, and never take `&rb[0]` on an
empty buffer.

See [**CONCEPTS.md**](./CONCEPTS.md), the helper
[`stlsoft/util/resizeable_buffer_helpers.hpp`](./include/stlsoft/util/resizeable_buffer_helpers.hpp),
and the example
[`examples/by_concept/resizeable_buffer/`](./examples/by_concept/resizeable_buffer/main.cpp).


<!-- ########################### end of file ########################### -->
18 changes: 9 additions & 9 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ There are three options for installing **STLSoft**, depending on how you obtain

If you obtain the latest release as an archive from the [**STLSoft** GitHub Releases](https://github.com/synesissoftware/STLSoft/releases) page (legacy archives may also still appear on [SourceForge](https://sourceforge.net/projects/stlsoft/)):

1. Download the latest distribution, e.g. [**STLSoft-1.11.1-rc9.zip**](https://github.com/synesissoftware/STLSoft/archive/refs/tags/1.11.1-rc9.zip);
1. Download the latest distribution, e.g. [**STLSoft-1.11.1-rc10.zip**](https://github.com/synesissoftware/STLSoft/archive/refs/tags/1.11.1-rc10.zip);

2. Unzip it to a directory of your choice, as in:

```bash
$ mkdir -p ~/open-source
$ cd ~/open-source
$ unzip STLSoft-1.11.1-rc9.zip
$ unzip STLSoft-1.11.1-rc10.zip
```

3. Define an environment variable `STLSOFT`, whose value is the directory in which you unzipped it, e.g. `STLSOFT=~/open-source/STLSoft-1.11.1-rc9`, and then specify `$(STLSOFT)/include` (**UNIX**) or `%STLSOFT%\include` (**Windows**) in your project files and makefiles;
3. Define an environment variable `STLSOFT`, whose value is the directory in which you unzipped it, e.g. `STLSOFT=~/open-source/STLSoft-1.11.1-rc10`, and then specify `$(STLSOFT)/include` (**UNIX**) or `%STLSOFT%\include` (**Windows**) in your project files and makefiles;


### Cloning project, using environment variables
Expand Down Expand Up @@ -100,16 +100,16 @@ $ sudo cmake --install ${SIS_CMAKE_BUILD_DIR:-./_build} --config Release
Unless you are certain that your projects, and all the projects upon which they depend, all use **STLSoft** via **CMake** then it is recommended that you define an `STLSOFT` environment variable.

For example, if you're using **UNIX** and you install to
**/usr/local/stlsoft/STLSoft-1.11.1-rc9**
**/usr/local/stlsoft/STLSoft-1.11.1-rc10**
then you should set the environment variable `STLSOFT` to
**/usr/local/stlsoft/STLSoft-1.11.1-rc9**
in which case the file `stlsoft/stlsoft.h`, for example, will be located in `/usr/local/stlsoft/STLSoft-1.11.1-rc9/include/stlsoft/stlsoft.h`.
**/usr/local/stlsoft/STLSoft-1.11.1-rc10**
in which case the file `stlsoft/stlsoft.h`, for example, will be located in `/usr/local/stlsoft/STLSoft-1.11.1-rc10/include/stlsoft/stlsoft.h`.

Conversely, if you're using **Windows** and you install to
**C:\3pty\STLSoft\STLSoft-1.11.1-rc9**
**C:\3pty\STLSoft\STLSoft-1.11.1-rc10**
then you should set the environment variable `STLSOFT` to
**C:\3pty\STLSoft\STLSoft-1.11.1-rc9**
in which case the file `stlsoft/stlsoft.h`, for example, will be located in `C:\3pty\STLSoft\STLSoft-1.11.1-rc9\include\stlsoft\stlsoft.h`.
**C:\3pty\STLSoft\STLSoft-1.11.1-rc10**
in which case the file `stlsoft/stlsoft.h`, for example, will be located in `C:\3pty\STLSoft\STLSoft-1.11.1-rc10\include\stlsoft\stlsoft.h`.

The makefiles / project files of several other open-source projects expect this symbol, including:

Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

| Date | News Item | Details |
| ------------------- | ----------------------------------- | ------- |
| 20th October 2026 | Release of [STLSoft 1.11.1-rc10](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc10) | Resizeable Buffer concept & traits `rb` hardening; **CONCEPTS.md**; `get_module_directory(rb)`; `resizeable_buffer_resize` docs; example **by_concept/resizeable_buffer**; C1–C5 `rb` / `dir_end` fixes; unixstl strip-all `remove_dir_end`; `readdir_sequence` no-exception soft-fail; full-path OOB size fix; `resizeable_buffer_resize` default-arg `resize` (G5) |
| 18th October 2026 | Release of [STLSoft 1.11.1-rc9](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc9) | `get_current_process_path()` (+ resizeable-buffer overloads) on **unixstl** / **winstl** `system_traits` with component tests; zero-first optional buffers; **unixstl** `wchar_t` path classification and separators; `path_str_*`; wide `get_home_directory()` and `get_environment_variable()`; `\pre` normalised to bare exprs with `nullptr`; subproject version bumps |
| 10th October 2026 | Release of [STLSoft 1.11.1-rc8](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc8) | environment_variable_strtoll()`; `unixstl::filesystem_traits<>::rewind_dir()`; `class_type` and `getenv_s()` empty-variable fixes; component tests; `cmake --build` helper scripts; **run_all_component_tests.sh** / **run_all_automated_tests.sh**; **generate_doxygen.sh**; Linguist classification |
| 30th September 2026 | Release of [STLSoft 1.11.1-rc7](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc7) | counted NULs; `wchar_t` `string_begins_with`; bit intrinsics; `write_binary()`; `file_lines` mmap (~10–20× vs `fgetc`); compare and allocator units; MinGW static runtime; short test directories; nested **test/performance** |
Expand Down
Loading
Loading