diff --git a/.gdbinit b/.gdbinit
new file mode 100644
index 0000000000..999e2fceb5
--- /dev/null
+++ b/.gdbinit
@@ -0,0 +1,2 @@
+# Load pretty printers
+source tools/gdb_pretty_printers/autoload.py
\ No newline at end of file
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
new file mode 100644
index 0000000000..9b1a2060df
--- /dev/null
+++ b/.github/workflows/cifuzz.yml
@@ -0,0 +1,26 @@
+name: CIFuzz
+on: [pull_request]
+jobs:
+ Fuzzing:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ id: build
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'immer'
+ dry-run: false
+ language: c++
+ - name: Run Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'immer'
+ fuzz-seconds: 300
+ dry-run: false
+ language: c++
+ - name: Upload Crash
+ uses: actions/upload-artifact@v3
+ if: failure() && steps.build.outcome == 'success'
+ with:
+ name: artifacts
+ path: ./out/artifacts
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 830892969a..253215f16d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,27 +7,33 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # needed for fetchGit in default.nix
- - uses: cachix/install-nix-action@v12
+ - uses: cachix/install-nix-action@v20
with:
nix_path: nixpkgs=channel:nixos-unstable
- install_url: "https://releases.nixos.org/nix/nix-2.3.16/install"
- - uses: cachix/cachix-action@v8
+ - uses: cachix/cachix-action@v12
with:
name: arximboldi
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
- run: nix-build
+ build-spm:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ - run: swift build
+
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- - uses: cachix/install-nix-action@v12
+ - uses: cachix/install-nix-action@v20
with:
nix_path: nixpkgs=channel:nixos-unstable
- install_url: "https://releases.nixos.org/nix/nix-2.3.16/install"
- - uses: cachix/cachix-action@v8
+ - uses: cachix/cachix-action@v12
with:
name: arximboldi
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
@@ -35,12 +41,12 @@ jobs:
- run: nix-shell --run "cd build && cmake .."
- run: nix-shell --run "cd build && make docs"
- uses: shimataro/ssh-key-action@v2
- if: github.ref == 'refs/heads/master'
+ if: github.ref == 'refs/heads/master' && github.repository_owner == 'arximboldi'
with:
key: ${{ secrets.SINUSOIDES_SSH_KEY }}
known_hosts: ${{ secrets.SINUSOIDES_KNOWN_HOSTS }}
- run: nix-shell --run "cd build && make upload-docs"
- if: github.ref == 'refs/heads/master'
+ if: github.ref == 'refs/heads/master' && github.repository_owner == 'arximboldi'
check:
strategy:
@@ -74,7 +80,7 @@ jobs:
- type: Debug
toolchain: llvm-8
std: 14
- opts: ['sanitizer']
+ opts: ['sanitize']
# benchmarks
- type: Release
toolchain: gnu-9
@@ -87,11 +93,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- - uses: cachix/install-nix-action@v12
+ - uses: cachix/install-nix-action@v20
with:
nix_path: nixpkgs=channel:nixos-unstable
- install_url: "https://releases.nixos.org/nix/nix-2.3.16/install"
- - uses: cachix/cachix-action@v8
+ - uses: cachix/cachix-action@v12
with:
name: arximboldi
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
@@ -113,11 +118,11 @@ jobs:
"
- run: nix-shell --argstr toolchain ${{ matrix.toolchain }} --run "cd build && make check -j`nproc`"
- run: nix-shell --argstr toolchain ${{ matrix.toolchain }} --run "bash <(curl -s https://codecov.io/bash)"
- if: ${{ contains(matrix.opts, 'coverage') }}
+ if: contains(matrix.opts, 'coverage')
- uses: shimataro/ssh-key-action@v2
- if: ${{ contains(matrix.opts, 'benchmark') }}
+ if: contains(matrix.opts, 'benchmark') && github.repository_owner == 'arximboldi'
with:
key: ${{ secrets.SINUSOIDES_SSH_KEY }}
known_hosts: ${{ secrets.SINUSOIDES_KNOWN_HOSTS }}
- run: nix-shell --run "cd build && make upload-benchmark-reports"
- if: ${{ contains(matrix.opts, 'benchmark') }}
+ if: contains(matrix.opts, 'benchmark') && github.repository_owner == 'arximboldi'
diff --git a/.gitignore b/.gitignore
index 2074141ec2..46c3c7c29f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,8 @@ __pycache__
tools/clojure/.lein*
*.pyc
+
+/result*
+
+.build
+.swiftpm
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2362ecb763..90ec3d9302 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_policy(SET CMP0048 NEW) # enable project VERSION
cmake_policy(SET CMP0056 NEW) # honor link flags in try_compile()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
-project(immer VERSION 0.7.0)
+project(immer VERSION 0.8.0)
if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-extended-offsetof -Wno-c++17-extensions -Wno-c++1z-extensions -Wno-unknown-warning-option -Wno-type-limits")
@@ -99,6 +99,14 @@ install(TARGETS immer EXPORT ImmerConfig)
install(EXPORT ImmerConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Immer")
install(DIRECTORY immer DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/ImmerConfigVersion.cmake"
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion )
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ImmerConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Immer" )
+
# development target to be used in tests, examples, benchmarks...
immer_canonicalize_cmake_booleans(
DISABLE_FREE_LIST
@@ -131,6 +139,8 @@ endif()
if (immer_BUILD_TESTS)
enable_testing()
+ find_package(Catch2 REQUIRED)
+
add_custom_target(check
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000000..af8254e8c4
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,29 @@
+// swift-tools-version:5.5
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "immer",
+ products: [
+ // Products define the executables and libraries a package produces, and make them visible to other packages.
+ .library(
+ name: "immer",
+ targets: ["immer"]),
+ ],
+ dependencies: [
+ // Dependencies declare other packages that this package depends on.
+ // .package(url: /* package url */, from: "1.0.0"),
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+ // Targets can depend on other targets in this package, and on products in packages this package depends on.
+ .target(
+ name: "immer",
+ dependencies: [],
+ path: ".",
+ sources: ["spm.cpp"],
+ publicHeadersPath: ".")
+ ],
+ cxxLanguageStandard: .cxx14
+)
diff --git a/README.rst b/README.rst
index abf4dceae1..834dbe6a30 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,6 @@
.. image:: https://github.com/arximboldi/immer/workflows/test/badge.svg
:target: https://github.com/arximboldi/immer/actions?query=workflow%3Atest+branch%3Amaster
- :alt: Github Actions Badge
+ :alt: GitHub Actions Badge
.. image:: https://codecov.io/gh/arximboldi/immer/branch/master/graph/badge.svg
:target: https://codecov.io/gh/arximboldi/immer
@@ -13,7 +13,10 @@
.. raw:: html
-
+
.. include:introduction/start
@@ -74,7 +77,7 @@ Example
For a **complete example** check `Ewig, a simple didactic
text-editor `_ built with this
library. You may also wanna check `Lager, a Redux-like library
- `_ for writting interactive
+ `_ for writing interactive
software in C++ using a value-oriented design.
diff --git a/benchmark/set/erase.hpp b/benchmark/set/erase.hpp
new file mode 100644
index 0000000000..a702fd25f8
--- /dev/null
+++ b/benchmark/set/erase.hpp
@@ -0,0 +1,85 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/config.hpp"
+
+#include
+#include // Phil Nash
+#include
+#include
+#include
+#include
+
+namespace {
+
+template
+auto benchmark_erase_mut_std()
+{
+ return [](nonius::chronometer meter) {
+ auto n = meter.param();
+ auto g = Generator{}(n);
+ auto v_ = [&] {
+ auto v = Set{};
+ for (auto i = 0u; i < n; ++i)
+ v.insert(g[i]);
+ return v;
+ }();
+ measure(meter, [&] {
+ auto v = v_;
+ for (auto i = 0u; i < n; ++i)
+ v.erase(g[i]);
+ return v;
+ });
+ };
+}
+
+template
+auto benchmark_erase()
+{
+ return [](nonius::chronometer meter) {
+ auto n = meter.param();
+ auto g = Generator{}(n);
+ auto v_ = [&] {
+ auto v = Set{}.transient();
+ for (auto i = 0u; i < n; ++i)
+ v.insert(g[i]);
+ return v.persistent();
+ }();
+ measure(meter, [&] {
+ auto v = v_;
+ for (auto i = 0u; i < n; ++i)
+ v = v.erase(g[i]);
+ return v;
+ });
+ };
+}
+
+template
+auto benchmark_erase_move()
+{
+ return [](nonius::chronometer meter) {
+ auto n = meter.param();
+ auto g = Generator{}(n);
+ auto v_ = [&] {
+ auto v = Set{}.transient();
+ for (auto i = 0u; i < n; ++i)
+ v.insert(g[i]);
+ return v.persistent();
+ }();
+ measure(meter, [&] {
+ auto v = v_;
+ for (auto i = 0u; i < n; ++i)
+ v = std::move(v).erase(g[i]);
+ return v;
+ });
+ };
+}
+
+} // namespace
diff --git a/benchmark/set/erase.ipp b/benchmark/set/erase.ipp
new file mode 100644
index 0000000000..54197744f0
--- /dev/null
+++ b/benchmark/set/erase.ipp
@@ -0,0 +1,44 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "erase.hpp"
+
+#ifndef GENERATOR_T
+#error "you must define a GENERATOR_T"
+#endif
+
+using generator__ = GENERATOR_T;
+using t__ = typename decltype(generator__{}(0))::value_type;
+
+// clang-format off
+NONIUS_BENCHMARK("std::set", benchmark_erase_mut_std>())
+NONIUS_BENCHMARK("std::unordered_set", benchmark_erase_mut_std>())
+NONIUS_BENCHMARK("boost::flat_set", benchmark_erase_mut_std>())
+// Phil Nash's hash_trie seems to not include an erase operation... at least at
+// the version that we have included in the nix-shell here...
+// NONIUS_BENCHMARK("hamt::hash_trie", benchmark_erase_mut_hash_trie>())
+
+NONIUS_BENCHMARK("immer::set/5B", benchmark_erase,std::equal_to,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/4B", benchmark_erase,std::equal_to,def_memory,4>>())
+#ifndef DISABLE_GC_BENCHMARKS
+NONIUS_BENCHMARK("immer::set/GC", benchmark_erase,std::equal_to,gc_memory,5>>())
+#endif
+NONIUS_BENCHMARK("immer::set/UN", benchmark_erase,std::equal_to,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("immer::set/move/5B", benchmark_erase_move,std::equal_to,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/move/4B", benchmark_erase_move,std::equal_to,def_memory,4>>())
+NONIUS_BENCHMARK("immer::set/move/UN", benchmark_erase_move,std::equal_to,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("immer::set/tran/5B", benchmark_erase_mut_std,std::equal_to,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/tran/4B", benchmark_erase_mut_std,std::equal_to,def_memory,4>>())
+#ifndef DISABLE_GC_BENCHMARKS
+NONIUS_BENCHMARK("immer::set/tran/GC", benchmark_erase_mut_std,std::equal_to,gc_memory,5>>())
+#endif
+NONIUS_BENCHMARK("immer::set/tran/UN", benchmark_erase_mut_std,std::equal_to,unsafe_memory,5>>())
+
+// clang-format on
diff --git a/benchmark/set/insert.hpp b/benchmark/set/insert.hpp
index be679b4de3..8950abfb82 100644
--- a/benchmark/set/insert.hpp
+++ b/benchmark/set/insert.hpp
@@ -10,9 +10,10 @@
#include "benchmark/config.hpp"
-#include
-#include // Phil Nash
#include
+#include // Phil Nash
+#include
+#include
#include
#include
@@ -21,8 +22,7 @@ namespace {
template
auto benchmark_insert_mut_std()
{
- return [] (nonius::chronometer meter)
- {
+ return [](nonius::chronometer meter) {
auto n = meter.param();
auto g = Generator{}(n);
@@ -38,8 +38,7 @@ auto benchmark_insert_mut_std()
template
auto benchmark_insert()
{
- return [] (nonius::chronometer meter)
- {
+ return [](nonius::chronometer meter) {
auto n = meter.param();
auto g = Generator{}(n);
@@ -52,4 +51,20 @@ auto benchmark_insert()
};
}
+template
+auto benchmark_insert_move()
+{
+ return [](nonius::chronometer meter) {
+ auto n = meter.param();
+ auto g = Generator{}(n);
+
+ measure(meter, [&] {
+ auto v = Set{};
+ for (auto i = 0u; i < n; ++i)
+ v = std::move(v).insert(g[i]);
+ return v;
+ });
+ };
+}
+
} // namespace
diff --git a/benchmark/set/insert.ipp b/benchmark/set/insert.ipp
index 6717ccd57e..cc6b648af3 100644
--- a/benchmark/set/insert.ipp
+++ b/benchmark/set/insert.ipp
@@ -13,8 +13,9 @@
#endif
using generator__ = GENERATOR_T;
-using t__ = typename decltype(generator__{}(0))::value_type;
+using t__ = typename decltype(generator__{}(0))::value_type;
+// clang-format off
NONIUS_BENCHMARK("std::set", benchmark_insert_mut_std>())
NONIUS_BENCHMARK("std::unordered_set", benchmark_insert_mut_std>())
NONIUS_BENCHMARK("boost::flat_set", benchmark_insert_mut_std>())
@@ -26,3 +27,16 @@ NONIUS_BENCHMARK("immer::set/4B", benchmark_insert,std::equal_to,gc_memory,5>>())
#endif
NONIUS_BENCHMARK("immer::set/UN", benchmark_insert,std::equal_to,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("immer::set/move/5B", benchmark_insert_move,std::equal_to,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/move/4B", benchmark_insert_move,std::equal_to,def_memory,4>>())
+NONIUS_BENCHMARK("immer::set/move/UN", benchmark_insert_move,std::equal_to,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("immer::set/tran/5B", benchmark_insert_mut_std,std::equal_to,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/tran/4B", benchmark_insert_mut_std,std::equal_to,def_memory,4>>())
+#ifndef DISABLE_GC_BENCHMARKS
+NONIUS_BENCHMARK("immer::set/tran/GC", benchmark_insert_mut_std,std::equal_to,gc_memory,5>>())
+#endif
+NONIUS_BENCHMARK("immer::set/tran/UN", benchmark_insert_mut_std,std::equal_to,unsafe_memory,5>>())
+
+// clang-format on
diff --git a/benchmark/set/memory/basic-string-long.cpp b/benchmark/set/memory/basic-string-long.cpp
new file mode 100644
index 0000000000..cd39f7f6ee
--- /dev/null
+++ b/benchmark/set/memory/basic-string-long.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_STRING_LONG 1
+
+#include "memory.hpp"
+
+int main() { return main_basic(); }
diff --git a/benchmark/set/memory/basic-string-short.cpp b/benchmark/set/memory/basic-string-short.cpp
new file mode 100644
index 0000000000..2c4d47cceb
--- /dev/null
+++ b/benchmark/set/memory/basic-string-short.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_STRING_SHORT 1
+
+#include "memory.hpp"
+
+int main() { return main_basic(); }
diff --git a/benchmark/set/memory/basic-unsigned.cpp b/benchmark/set/memory/basic-unsigned.cpp
new file mode 100644
index 0000000000..0b02c40424
--- /dev/null
+++ b/benchmark/set/memory/basic-unsigned.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_UNSIGNED 1
+
+#include "memory.hpp"
+
+int main() { return main_basic(); }
diff --git a/benchmark/set/memory/exp-string-long.cpp b/benchmark/set/memory/exp-string-long.cpp
new file mode 100644
index 0000000000..866869d9e0
--- /dev/null
+++ b/benchmark/set/memory/exp-string-long.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_STRING_LONG 1
+
+#include "memory.hpp"
+
+int main() { return main_exp(); }
diff --git a/benchmark/set/memory/exp-string-short.cpp b/benchmark/set/memory/exp-string-short.cpp
new file mode 100644
index 0000000000..e198c07056
--- /dev/null
+++ b/benchmark/set/memory/exp-string-short.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_STRING_SHORT 1
+
+#include "memory.hpp"
+
+int main() { return main_exp(); }
diff --git a/benchmark/set/memory/exp-unsigned.cpp b/benchmark/set/memory/exp-unsigned.cpp
new file mode 100644
index 0000000000..2bed0de5d9
--- /dev/null
+++ b/benchmark/set/memory/exp-unsigned.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_UNSIGNED 1
+
+#include "memory.hpp"
+
+int main() { return main_exp(); }
diff --git a/benchmark/set/memory/lin-string-long.cpp b/benchmark/set/memory/lin-string-long.cpp
new file mode 100644
index 0000000000..ce3d5bd161
--- /dev/null
+++ b/benchmark/set/memory/lin-string-long.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_STRING_LONG 1
+
+#include "memory.hpp"
+
+int main() { return main_lin(); }
diff --git a/benchmark/set/memory/lin-string-short.cpp b/benchmark/set/memory/lin-string-short.cpp
new file mode 100644
index 0000000000..f1a610b854
--- /dev/null
+++ b/benchmark/set/memory/lin-string-short.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_STRING_SHORT 1
+
+#include "memory.hpp"
+
+int main() { return main_lin(); }
diff --git a/benchmark/set/memory/lin-unsigned.cpp b/benchmark/set/memory/lin-unsigned.cpp
new file mode 100644
index 0000000000..c3e9bad2f2
--- /dev/null
+++ b/benchmark/set/memory/lin-unsigned.cpp
@@ -0,0 +1,5 @@
+#define IMMER_BENCHMARK_MEMORY_UNSIGNED 1
+
+#include "memory.hpp"
+
+int main() { return main_lin(); }
diff --git a/benchmark/set/memory/memory.hpp b/benchmark/set/memory/memory.hpp
new file mode 100644
index 0000000000..7b218ba15f
--- /dev/null
+++ b/benchmark/set/memory/memory.hpp
@@ -0,0 +1,451 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+//
+// These are some experiments to get insights about memory usage with various
+// data-structures and configurations.
+//
+// The idea is to run this inside valgrind's massif tool and see what comes
+// out. The following is the libraries that we do check.
+//
+
+// these are for "exp" tests
+#include
+#include // Phil Nash
+#include
+#include
+#include
+#include
+
+// these are for "lin" tests, which are map based actually
+#include
+#include // Phil Nash
+#include
+#include
+#include