-
-
- - Marcos Pontes - - |
-
-
-
- - Alan De Freitas - - |
+
+
+ + Marcos Pontes + + |
+
+
+
+ + Alan De Freitas + + |
around cpp reference links regardless of macros
+ let cpp_reference_links = Array.from(document.querySelectorAll('.md-typeset a[href*="cppreference"]'));
+ let open_std_links = Array.from(document.querySelectorAll('.md-typeset a[href*="open-std"]'));
+ let small_reference_link = Array.from(document.querySelectorAll('.md-typeset a[href*="/small/reference/"]'));
+ for (const link of cpp_reference_links.concat(open_std_links).concat(small_reference_link)) {
+ let already_inside_code_tag = link.parentElement.tagName !== undefined && link.parentElement.tagName.toUpperCase() === 'CODE'
+ let already_has_code_tag = link.firstChild.tagName !== undefined && link.firstChild.tagName.toUpperCase() === 'CODE'
+ let is_code_reference = link.innerHTML.startsWith('std::') || link.href.includes('/small/reference/')
+ if (already_inside_code_tag || already_has_code_tag || !is_code_reference) {
+ continue;
+ }
+ link.innerHTML = "" + link.innerHTML + "
"
+ }
+
+ // Fix bug in doxybook reference anchors
+ let brokenAnchors = ['#function-', '#variable-', '#using-']
+ for (const anchor of brokenAnchors) {
+ for (const link of small_reference_link) {
+ anchorPos = link.href.lastIndexOf(anchor);
+ if (anchorPos !== -1) {
+ let splitPos = anchorPos + anchor.length;
+ let linkLhs = link.href.substr(0, splitPos);
+ let linkRhs = link.href.substr(splitPos);
+ link.href = linkLhs + linkRhs.replaceAll('-', '_');
+ }
+ }
+ }
+})
\ No newline at end of file
diff --git a/docs/macros.py b/docs/macros.py
new file mode 100644
index 0000000..2723bd1
--- /dev/null
+++ b/docs/macros.py
@@ -0,0 +1,76 @@
+import os
+
+def declare_variables(variables, macro):
+ @macro
+ def code_snippet(filename: str, snippet: str = "", language: str = "cpp"):
+ """
+ Load code from a file and save as a preformatted code block.
+ If a language is specified, it's passed in as a hint for syntax highlighters.
+ Example usage in markdown:
+ {{code_from_file("code/myfile.py", "python")}}
+ """
+ docs_dir = variables.get("docs_dir", "docs")
+
+ # Look for file
+ abs_docs_path = os.path.abspath(os.path.join(docs_dir, filename))
+ abs_root_path = os.path.abspath(os.path.join(docs_dir, "..", filename))
+ abs_examples_path = os.path.abspath(os.path.join(docs_dir, "../examples/", filename))
+ abs_path = ''
+ if os.path.exists(abs_docs_path):
+ abs_path = abs_docs_path
+ elif os.path.exists(abs_root_path):
+ abs_path = abs_root_path
+ elif os.path.exists(abs_examples_path):
+ abs_path = abs_examples_path
+
+ # File not found
+ if not os.path.exists(abs_path):
+ return f"""File not found: {filename}"""
+
+ # Read snippet from file
+ with open(abs_path, "r") as f:
+ if not snippet:
+ return (
+ f"""```{language}\n{f.read()}\n```"""
+ )
+ else:
+ # Extract the snippet
+ contents = f.read()
+ start_pos = contents.find('//[' + snippet)
+ if start_pos == -1:
+ return f"""Snippet {snippet} not found in {filename}"""
+ end_pos = contents.find('//]', start_pos)
+ if end_pos == -1:
+ return f"""Snippet {snippet} not found in {filename}"""
+ contents = contents[(start_pos + 3 + len(snippet)):(end_pos - 3)]
+
+ # Identify snippet header
+ content_lines = contents.splitlines()
+ first_line = content_lines[0]
+ header = ''
+ if not first_line.isspace() and not len(first_line) == 0:
+ header = first_line.strip()
+
+ # Identify indent
+ indent_size = 20
+ for line in content_lines[1:]:
+ if not line.isspace() and not len(line) == 0:
+ first_char_pos = len(line) - len(line.lstrip())
+ indent_size = min(indent_size, first_char_pos)
+
+ # Construct snippet
+ contents = ''
+ if len(header) != 0:
+ contents += '=== "' + header + '"\n\n '
+ contents += '```' + language
+ if len(content_lines) > 10:
+ contents += ' linenums="1" '
+ contents += '\n'
+ for line in content_lines[1:]:
+ if len(header) != 0:
+ contents += ' '
+ contents += line[indent_size:] + '\n'
+ if len(header) != 0:
+ contents += ' '
+ contents += '```\n'
+ return contents
\ No newline at end of file
diff --git a/docs/quickstart.md b/docs/quickstart.md
new file mode 100644
index 0000000..a9ecb98
--- /dev/null
+++ b/docs/quickstart.md
@@ -0,0 +1,190 @@
+# Quickstart
+
+Integration:
+
+=== "CMake"
+
+ === "Add subdirectory"
+
+ ```bash
+ git clone https://github.com/alandefreitas/small/
+ ```
+
+ ```cmake
+ add_subdirectory(small)
+ # ...
+ add_executable(your_target main.cpp)
+ target_link_libraries(your_target PUBLIC small::small)
+ ```
+
+ === "Fetch content"
+
+ ```cmake
+ include(FetchContent)
+
+ FetchContent_Declare(small
+ GIT_REPOSITORY https://github.com/alandefreitas/small
+ GIT_TAG origin/master # or whatever tag you want
+ )
+
+ FetchContent_GetProperties(small)
+ if(NOT small_POPULATED)
+ FetchContent_Populate(small)
+ add_subdirectory(${small_SOURCE_DIR} ${small_BINARY_DIR} EXCLUDE_FROM_ALL)
+ endif()
+
+ # ...
+ add_executable(your_target main.cpp)
+ target_link_libraries(your_target PUBLIC small::small)
+ ```
+
+ === "External package"
+
+ ```cmake
+ # Follow installation instructions and then...
+ find_package(small REQUIRED)
+ if(NOT small_FOUND)
+ # Throw or put your FetchContent script here
+ endif()
+
+ # ...
+ add_executable(your_target main.cpp)
+ target_link_libraries(your_target PUBLIC small::small)
+ ```
+
+=== "Install"
+
+ !!! note
+
+ Get the binary package from the [release section](https://github.com/alandefreitas/small/releases).
+
+ These binaries refer to the latest release version of small.
+
+ !!! hint
+
+ If you need a more recent version of `small`, you can download the binary packages from the CI artifacts or build the library from the source files.
+
+=== "Build from source"
+
+ !!! note
+
+ Ensure your C++ compiler and CMake are up-to-date and then:
+
+ === "Ubuntu + GCC"
+
+ ```bash
+ # Create a new directory
+ mkdir build
+ cd build
+ # Configure
+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2"
+ # Build
+ sudo cmake --build . --parallel 2 --config Release
+ # Install
+ sudo cmake --install .
+ # Create packages
+ sudo cpack .
+ ```
+
+ === "Mac Os + Clang"
+
+ ```bash
+ # Create a new directory
+ mkdir build
+ cd build
+ # Configure
+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2"
+ # Build
+ cmake --build . --parallel 2 --config Release
+ # Install
+ cmake --install .
+ # Create packages
+ cpack .
+ ```
+
+ === "Windows + MSVC"
+
+ ```bash
+ # Create a new directory
+ mkdir build
+ cd build
+ # Configure
+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="/O2"
+ # Build
+ cmake --build . --parallel 2 --config Release
+ # Install
+ cmake --install .
+ # Create packages
+ cpack .
+ ```
+
+ !!! hint "Parallel Build"
+
+ Replace `--parallel 2` with `--parallel `
+
+ !!! note "Setting C++ Compiler"
+
+ If your C++ compiler that supports C++17 is not your default compiler, make sure you provide CMake with the compiler location with the DCMAKE_C_COMPILER and DCMAKE_CXX_COMPILER options. For instance:
+
+ ```bash
+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2" -DCMAKE_C_COMPILER=/usr/bin/gcc-8 -DCMAKE_CXX_COMPILER=/usr/bin/g++-8
+ ```
+
+=== "File amalgamation"
+
+ !!! note
+
+ Because containers are header-only, you can directly copy the contents from the `source` directory into your project.
+
+ !!! hint
+
+ In that case, you are responsible for setting the appropriate target include directories and any compile definitions you might require.
+
+
+Once the library is properly integrated, you can create containers from the namespace `small` like any other STL container:
+
+```cpp
+--8<-- "examples/main.cpp"
+```
+
+All containers are optimized for the case when they're small but also efficient when they are large. The containers mix the common techniques found in other small container libraries:
+
+- Inline allocation for small containers
+- Custom expected sizes
+- Identification of relocatable types
+- Custom growth factors with better defaults
+- Communication with system memory allocators
+- Explicit consideration of CPU cache sizes and branch prediction
+
+Most applications have many small lists and sets of elements. These containers avoid spending a lot of time with large containers
+that contain just a few elements. Small containers usually try to use the stack before dynamically allocating memory and try
+to represent associative containers with stack arrays, unless these sets are very large.
+
+The following containers are available:
+
+- `small::vector`
+- `small::max_size_vector`
+- `small::string`
+- `small::set`
+- `small::max_size_set`
+- `small::multiset`
+- `small::max_size_multiset`
+- `small::unordered_set`
+- `small::max_size_unordered_set`
+- `small::unordered_multiset`
+- `small::max_size_unordered_multiset`
+- `small::map`
+- `small::max_size_map`
+- `small::multimap`
+- `small::max_size_multimap`
+- `small::unordered_map`
+- `small::max_size_unordered_map`
+- `small::unordered_multimap`
+- `small::max_size_unordered_multimap`
+- `small::stack`
+- `small::queue`
+- `small::priority_queue`
+
+Although many compilers support small string optimization (SSO) already, this library will ensure all strings support SOO, custom inline sizes, relocation, and unicode.
+
+--8<-- "docs/references.md"
\ No newline at end of file
diff --git a/docs/references.md b/docs/references.md
new file mode 100644
index 0000000..f6ebfe8
--- /dev/null
+++ b/docs/references.md
@@ -0,0 +1,30 @@
+
+[constexpr]: https://en.cppreference.com/w/cpp/language/constexpr
+
+
+[std::async]: https://en.cppreference.com/w/cpp/thread/async
+[std::future]: https://en.cppreference.com/w/cpp/thread/future
+[std::future_status]: https://en.cppreference.com/w/cpp/thread/future_status
+[std::packaged_task]: https://en.cppreference.com/w/cpp/thread/packaged_task
+[std::promise]: https://en.cppreference.com/w/cpp/thread/promise
+[std::future::wait]: https://en.cppreference.com/w/cpp/thread/future/wait
+[std::future::get]: https://en.cppreference.com/w/cpp/thread/future/get
+[std::shared_future]: https://en.cppreference.com/w/cpp/thread/shared_future
+[std::stop_source]: https://en.cppreference.com/w/cpp/thread/stop_source
+[std::stop_token]: https://en.cppreference.com/w/cpp/thread/stop_token
+[std::thread]: https://en.cppreference.com/w/cpp/thread/thread
+[std::jthread]: https://en.cppreference.com/w/cpp/thread/jthread
+[std::future]: https://en.cppreference.com/w/cpp/thread/future
+[std::shared_future]: https://en.cppreference.com/w/cpp/thread/shared_future
+[std::stop_source]: https://en.cppreference.com/w/cpp/thread/stop_source
+[std::stop_token]: https://en.cppreference.com/w/cpp/thread/stop_token
+[std::jthread]: https://en.cppreference.com/w/cpp/thread/jthread
+[std::is_constant_evaluated]: https://en.cppreference.com/w/cpp/types/is_constant_evaluated
+[std::vector]: https://en.cppreference.com/w/cpp/container/vector
+[std::tuple]: https://en.cppreference.com/w/cpp/utility/tuple
+
+
+
+
+
+
diff --git a/docs/sets_and_maps.md b/docs/sets_and_maps.md
new file mode 100644
index 0000000..cd6dbe7
--- /dev/null
+++ b/docs/sets_and_maps.md
@@ -0,0 +1,15 @@
+# Sets and Maps
+
+The small set/map classes use a more cache-friendly flat set/map and all other optimizations mentioned above for internal algorithms. As with other small containers, a custom template parameter can be used to define the number of inline elements in the container.
+
+The `small::default_inline_storage` and `small::is_relocatable` trait can also be defined for custom types, and all the usual set/map, ordered/unordered, uni/multi variants are also provided:
+
+```cpp
+--8<-- "examples/associative.cpp"
+```
+
+Unlike a `small::vector` or `small::string`, the asymptotic time complexities of flat sets/maps are very different from their `std::` counterparts and should only be used when they are small. Because they are internally implemented as arrays, manipulating these containers costs `O(n)`.
+
+For large containers, you can use `std` containers with custom allocators. Or for efficient large containers, you can use the abseil containers, implemented as B+-trees.
+
+--8<-- "docs/references.md"
\ No newline at end of file
diff --git a/docs/strings.md b/docs/strings.md
new file mode 100644
index 0000000..d9dd98a
--- /dev/null
+++ b/docs/strings.md
@@ -0,0 +1,17 @@
+# Strings
+
+The small string includes all the common optimizations for small vectors, and a custom template parameter to set how large we expect the string to be (in bytes).
+
+However, when strings are representing text, if there's one thing that makes them not small is not supporting UTF8. In addition to the common interface for strings, `small::string` includes extra functions to identify and work with UTF8 code points with random access.
+
+```cpp
+--8<-- "examples/unicode_strings.cpp"
+```
+
+The problem of supporting UTF8 is easier to explain than it is to solve. Programming languages tend to solve this problem by (1) forbidding byte or substring access, and/or (2) allowing only access to code points with `O(n)` cost, where `n` is the number of code points. Because anything that forbids byte access would be incompatible with a C++ string, we allow direct byte access, and strings are allowed to be malformed unicode, which we can check with `small::is_malformed`.
+
+All capacity and access functions contain extra overloads that accept codepoint indexes, defined as a strong type, rather than byte indexes. By using these functions, one can ensure the string is never malformed. It's up to the user to decide whether these access functions are useful and worth it in a particular application.
+
+Access to codepoints is provided with an inline lookup-table trick that allows us to access codepoints in `O(log m)` time, where `m` is the number of multibyte code points in the strings. When there are no multibyte codepoints in the string, the string works as usual and no extra memory is required for the table.
+
+--8<-- "docs/references.md"
\ No newline at end of file
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
new file mode 100644
index 0000000..224bc3d
--- /dev/null
+++ b/docs/stylesheets/extra.css
@@ -0,0 +1,10 @@
+[data-md-color-scheme="small"] {
+ --md-primary-fg-color: #4d90ab;
+ --md-primary-fg-color--light: #5B91A3;
+ --md-primary-fg-color--dark: #5B91A3;
+ --md-typeset-a-color: var(--md-primary-fg-color--light);
+ --md-accent-fg-color: var(--md-primary-fg-color--dark);
+ --md-accent-fg-color--transparent: #dcdcdc;
+ --md-accent-bg-color: hsla(0, 0%, 100%, 1);
+ --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);
+}
\ No newline at end of file
diff --git a/docs/vectors.md b/docs/vectors.md
new file mode 100644
index 0000000..30a90a6
--- /dev/null
+++ b/docs/vectors.md
@@ -0,0 +1,27 @@
+# Vectors
+
+This small vector implementation includes:
+
+- Inline allocation for small vectors
+- Custom expected size
+- Special treatment of relocatable types
+ - Relocatable types can be moved with `memcpy`, bypassing destructors and constructors.
+ - Relocatable types are defined by default for POD types and aggregate types of PODs
+ - The `small::is_relocatable` traits can be used as an extension point for custom types
+- Better growth factors
+- Consider the cache line size in allocations
+- Heap allocations can be disabled with `small::max_size_vector`
+
+When there are fewer elements than a given threshold, the elements are kept in a stack buffer for small vectors. Otherwise, the vector works as usual. However, if you are 100% sure you will never need more than `N` elements, you can use a `max_size_vector`, where elements are always inline.
+
+The default number of elements in a small vector is usually the number of elements we can already fit inline in a vector. For larger data types, the `default_inline_storage` trait can be used as an extension point where one can define how many elements a small vector of that type should contain by default.
+
+```cpp
+--8<-- "examples/default_inline_storage.cpp"
+```
+
+When there's a reasonable default for the number of inline elements, this strategy avoids multiple vector type instantiations for different inline storage sizes.
+
+This small vector implementation used folly, abseil, and LLVM as a reference.
+
+--8<-- "docs/references.md"
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index eeeed7a..88d4975 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -5,11 +5,27 @@ repo_name: alandefreitas/small
site_description: "Small: C++ Small Containers"
+nav:
+ - Home: index.md
+ - Quickstart: quickstart.md
+ - Vectors: vectors.md
+ - Strings: strings.md
+ - Sets and Maps: sets_and_maps.md
+ - Reference: # Reference is organized by module
+ - Small: reference/Modules/index.md # futures module
+ - basic_string: reference/Classes/classsmall_1_1basic__string.md
+ - vector: reference/Classes/classsmall_1_1vector.md
+ - Classes: reference/Classes/index.md # All Classes
+ - Files: reference/Files/index.md
+ - Index: reference/Namespaces/namespacesmall.md # The futures namespace contains everything
+ - Contributing: contributing.md
+
+
theme:
name: material
custom_dir: docs/overrides
palette:
- scheme: preference
+ scheme: small
icon:
repo: fontawesome/brands/git-alt
logo: fontawesome/solid/code
@@ -18,11 +34,15 @@ edit_uri: ""
plugins:
- search
- - awesome-pages
+ # - mermaid2
+ - macros:
+ module_name: docs/macros
+ include_dir: ..
-google_analytics:
- - UA-109858331-6
- - auto
+extra:
+ analytics:
+ provider: google
+ property: G-290016612
copyright: Copyright © Alan Freitas
@@ -31,11 +51,12 @@ markdown_extensions:
- pymdownx.details
- pymdownx.highlight:
use_pygments: true
- linenums: true
+ linenums: false
linenums_style: pymdownx.inline
- pymdownx.inlinehilite
- pymdownx.superfences
- - pymdownx.tabbed
+ - pymdownx.tabbed:
+ alternate_style: true
- pymdownx.snippets
- pymdownx.arithmatex:
generic: true
@@ -44,4 +65,8 @@ markdown_extensions:
extra_javascript:
- https://polyfill.io/v3/polyfill.min.js?features=es6
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
- - https://media.ethicalads.io/media/client/ethicalads.min.js
\ No newline at end of file
+ - https://media.ethicalads.io/media/client/ethicalads.min.js
+ - javascripts/extra.js
+
+extra_css:
+ - stylesheets/extra.css
\ No newline at end of file