Merge branch 'main' of https://github.com/FreeCAD/FreeCAD into refactor

This commit is contained in:
Lawrence Woestman
2025-04-22 11:17:18 -07:00
1464 changed files with 71017 additions and 174426 deletions

View File

@@ -0,0 +1,47 @@
<!--
The FreeCAD community thanks you for your contribution!
By creating a Pull Request you agree to the contributing policy. The complete policy can be found in the root of the source tree (CONTRIBUTING.md) or at https://github.com/FreeCAD/FreeCAD/blob/main/CONTRIBUTING.md
This template provides guidance on creating a PR that can be reviewed and approved as quickly as possible. Comments may be safely deleted.
Unless you know exactly what you're doing, please leave the checkbox 'Allow edits by maintainers' enabled. This will allow maintainers to help you.
-->
## Description
<!-- Include a brief summary of the changes. -->
## Issues
<!-- link to individual issues this PR closes by referencing the issue number (e.g., fixes #1234, closes #4321). -->
## Before and After Images
<!-- If your proposed changes affect the FreeCAD GUI, add before and after screenshots -->
<!-- Notes on the PR Review Process
The following section describes what the maintainers consider when reviewing your Pull Request. These items may not require you to take any action. This information is provided for context. Understanding what we consider will help you prepare your request for speedy approval.
You can find additional documentation about these guidelines in the [Developers handbook](https://freecad.github.io/DevelopersHandbook).
Alignment (Does the PR align with the goals and interests of the project?)
- Does the PR have at least one issue linked, which this PR closes?
- Has the conversation on the PR and related issue(s) reached consensus?
- If the PR affects the GUI, is the Design Working Group (DWG) aware and have they had time to review and comment?
- If the PR affects the GUI, did the contributor include before/after images?
- If the PR affects standards and workflow, is the CAD Working Group (CWG) aware and have they had time to review/comment?
Impact (Does the change affect other parts of the project?)
- Has the impact on documentation been considered and appropriate action taken?
- Has the impact on translation been considered appropriate action taken?
- Will the PR affect existing user documents?
Code Quality (Is code well-written and maintainable?)
- Does the PR warrant a review by the Code Quality Working Group (CQWG)?
- Does the change include tests?
- Is the PR rebased on the current main branch with unnecessary commits squashed?
Release (Are there considerations related to release timing?)
- Has the PR been considered for backporting to the latest release branch?
- Have the release notes been considered/updated?
-->

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
- package-ecosystem: pip
directory: /
schedule:
interval: daily

View File

@@ -57,6 +57,11 @@ jobs:
env:
logdir: /tmp/log/
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Make needed directories
run: |
mkdir -p ${{ env.logdir }}
@@ -103,7 +108,7 @@ jobs:
done
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ github.job }}-Logs
path: |

View File

@@ -117,6 +117,13 @@ runs:
testCommand: ${{ inputs.builddir }}/tests/Sketcher_tests_run --gtest_output=json:${{ inputs.reportdir }}spreadsheet_gtest_results.json
testLogFile: ${{ inputs.reportdir }}spreadsheet_gtest_test_log.txt
testName: Spreadsheet
- name: C++ Start tests
id: start
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Start_tests_run --gtest_output=json:${{ inputs.reportdir }}start_gtest_results.json
testLogFile: ${{ inputs.reportdir }}start_gtest_test_log.txt
testName: Start
- name: Compose summary report based on test results
if: always()
shell: bash -l {0}

View File

@@ -41,11 +41,11 @@ inputs:
libpackdownloadurl:
description: "URL where to download libpack"
required: false
default: https://github.com/FreeCAD/FreeCAD-LibPack/releases/download/3.1.0/LibPack-1.1.0-v3.1.0-Release.7z
default: https://github.com/FreeCAD/FreeCAD-LibPack/releases/download/3.1.1.2/LibPack-1.1.0-v3.1.1.2-Release.7z
libpackname:
description: "Libpack name (once downloaded)"
required: false
default: LibPack-1.1.0-v3.1.0-Release
default: LibPack-1.1.0-v3.1.1.2-Release
runs:
using: "composite"

View File

@@ -20,9 +20,14 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: '🧹 Tag & close stale unconfirmed bugs'
id: stale_issues
uses: actions/stale@v9.1.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
@@ -49,7 +54,7 @@ jobs:
- name: '🧹 Close stale requested feedback issues'
id: awaiting_issues
uses: actions/stale@v9.1.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
@@ -77,7 +82,7 @@ jobs:
- name: '🧹 Tag & close inactive issues'
id: inactive_issues
uses: actions/stale@v9.1.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
@@ -108,7 +113,7 @@ jobs:
- name: '🧹 Tag & close inactive PRs'
id: inactive_pr
uses: actions/stale@v9.1.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1

27
.github/workflows/dependency-review.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request,
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
# Once installed, if the workflow run is marked as required,
# PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: 'Checkout Repository'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 'Dependency Review'
uses: actions/dependency-review-action@67d4f4bd7a9b17a0db54d2a7519187c65e339de8 # v4

View File

@@ -16,6 +16,11 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Get dates for last month
shell: bash
run: |
@@ -30,13 +35,13 @@ jobs:
echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"
- name: Run issue-metrics tool
uses: github/issue-metrics@v3
uses: github/issue-metrics@ed6e4b2b46f44ec7cdd2f6c145757a67a07ecf5b # v3.18.4
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}'
- name: Create issue
uses: peter-evans/create-issue-from-file@v4
uses: peter-evans/create-issue-from-file@e8ef132d6df98ed982188e460ebb3b5d4ef3a9cd # v5.0.1
with:
title: Monthly issue metrics report
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -9,6 +9,9 @@ on:
pull_request_target:
types: [opened, reopened]
permissions:
contents: read
jobs:
label:
runs-on: ubuntu-latest
@@ -17,7 +20,12 @@ jobs:
pull-requests: write
steps:
- uses: actions/labeler@v5
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: ".github/labels.yml"

81
.github/workflows/scorecards.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '20 7 * * 2'
push:
branches: ["main"]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
contents: read
actions: read
# To allow GraphQL ListCommits to work
issues: read
pull-requests: read
# To detect SAST tools
checks: read
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecards on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
with:
sarif_file: results.sarif

View File

@@ -69,6 +69,11 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
@@ -81,7 +86,7 @@ jobs:
fi
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Add GCC Problem Matcher
if: runner.os == 'Linux'
@@ -106,13 +111,13 @@ jobs:
mkdir -p ${{ env.reportdir }}
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- uses: prefix-dev/setup-pixi@v0.8.3
- uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
with:
pixi-version: v0.43.3
pixi-version: v0.45.0
cache: false
- name: Restore Compiler Cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
@@ -195,14 +200,14 @@ jobs:
- name: Save Compiler Cache
if: always()
uses: actions/cache/save@v4
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifactBasename }}-${{ matrix.os }}-Logs
path: |
@@ -211,7 +216,7 @@ jobs:
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.reportfilename }}
path: |

View File

@@ -71,8 +71,13 @@ jobs:
reportFile: ${{ steps.Init.outputs.reportFile }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Checking out source code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: true
- name: Install FreeCAD dependencies
@@ -94,7 +99,7 @@ jobs:
compiler: ${{ env.CXX }}
qt_major_version: 5
- name: Restore Compiler Cache
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
save-always: true
path: ${{ env.CCACHE_DIR }}
@@ -107,6 +112,10 @@ jobs:
ccache -s
ccache -z
ccache -p
- name: Install cmake
uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be # v2.0.2
with:
cmake-version: '3.31.6'
- name: CMake Configure
uses: ./.github/workflows/actions/linux/configure
with:
@@ -175,7 +184,7 @@ jobs:
reportFile: ${{env.reportdir}}${{ env.reportfilename }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
@@ -183,7 +192,7 @@ jobs:
/var/crash/*FreeCAD*
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.reportfilename }}
path: |

View File

@@ -62,8 +62,13 @@ jobs:
reportFile: ${{ steps.Init.outputs.reportFile }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Checking out source code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: true
- name: Make needed directories, files and initializations
@@ -85,7 +90,7 @@ jobs:
with:
libpackdir: ${{ env.libpackdir }}
- name: Restore compiler cache
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
save-always: true
path: ${{ env.CCACHE_DIR }}
@@ -98,6 +103,10 @@ jobs:
. $env:ccachebindir\ccache -s
. $env:ccachebindir\ccache -z
. $env:ccachebindir\ccache -p
- name: Install cmake
uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be # v2.0.2
with:
cmake-version: '3.31.6'
- name: Configuring CMake
run: >
cmake -B"${{ env.builddir }}" .
@@ -111,7 +120,7 @@ jobs:
-DFREECAD_COPY_LIBPACK_BIN_TO_BUILD=ON
-DFREECAD_COPY_PLUGINS_BIN_TO_BUILD=ON
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0
- name: Compiling sources
run: |
cd $env:builddir
@@ -129,7 +138,7 @@ jobs:
. ${{ env.builddir }}\bin\FreeCADCmd -t 0 # 2>&1 | tee -filepath ${{ env.logdir }}\integrationTests.log
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |

View File

@@ -121,7 +121,7 @@ on:
type: string
required: false
spellingIgnore:
default: ./.git,*.po,*.ts,*.svg,./ChangeLog.txt,./src/3rdParty,./src/Mod/Assembly/App/opendcm,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL*,./src/WindowsInstaller,./src/Doc/FreeCAD.uml,./build/
default: ./.git*,*.po,*.ts,*.svg,./src/3rdParty,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL*,./src/Doc/FreeCAD.uml,./build/
type: string
required: false
codespellFailSilent:
@@ -168,6 +168,9 @@ on:
reportFile:
value: ${{ jobs.Lint.outputs.reportFile }}
permissions:
contents: read
jobs:
Lint:
@@ -185,8 +188,13 @@ jobs:
reportFile: ${{ steps.Init.outputs.reportFile }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: true
@@ -335,7 +343,7 @@ jobs:
- name: Upload logs and fixes
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
@@ -344,7 +352,7 @@ jobs:
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.reportfilename }}
path: |

View File

@@ -71,6 +71,11 @@ jobs:
changedCppFiles: ${{ steps.Output.outputs.changedCppFiles }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Make needed directories, files and initializations
id: Init
run: |
@@ -148,14 +153,14 @@ jobs:
echo "" >> $GITHUB_OUTPUT
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
${{ env.logdir }}
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.reportfilename }}
path: |

160
.github/workflows/sub_weeklyBuild.yml vendored Normal file
View File

@@ -0,0 +1,160 @@
name: Weekly Build
on:
schedule:
- cron: "42 18 * * 1"
permissions:
contents: write
jobs:
tag_build:
runs-on: ubuntu-latest
outputs:
build_tag: ${{ steps.tag_build.outputs.build_tag }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 2
submodules: 'recursive'
- name: Tag Build
id: tag_build
shell: bash -l {0}
run: |
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT"
- name: Upload Source
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git config user.name 'github-actions[bot]'
git apply package/rattler-build/scripts/disable_git_info.patch
git commit -a -m "Disable git info write to Version.h"
git archive HEAD -o freecad_source_${BUILD_TAG}.tar
git submodule foreach --recursive \
"git archive HEAD --prefix=\$path/ -o \$sha1.tar && \
tar -A -f \$toplevel/freecad_source_${BUILD_TAG}.tar \$sha1.tar && \
rm \$sha1.tar"
gzip freecad_source_${BUILD_TAG}.tar
sha256sum freecad_source_${BUILD_TAG}.tar.gz > freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt
gh release create ${BUILD_TAG} --title "Weekly Build ${BUILD_TAG}" --notes "Weekly Build ${BUILD_TAG}" --prerelease || true
gh release upload --clobber ${BUILD_TAG} "freecad_source_${BUILD_TAG}.tar.gz" "freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt"
build:
needs: tag_build
strategy:
matrix:
include:
- { target: linux-64, os: ubuntu-22.04 }
- { target: linux-arm64, os: ubuntu-22.04-arm }
- { target: osx-64, os: macos-13 }
- { target: osx-arm64, os: macos-latest }
- { target: win-64, os: windows-latest }
fail-fast: false
runs-on: ${{ matrix.os }}
environment: weekly-build
steps:
# prevent running out of disk space on Ubuntu runners.
- name: Maximize build space
if: runner.os == 'Linux'
uses: AdityaGarg8/remove-unwanted-software@v5
with:
verbose: 'true'
remove-android: 'true' # (frees ~9 GB)
remove-cached-tools: 'true' # (frees ~8.3 GB)
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
BUILD_TAG: ${{ needs.tag_build.outputs.build_tag }}
OPERATING_SYSTEM: ${{ runner.os }}
run: |
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
if [[ $OPERATING_SYSTEM == 'Windows' ]]; then
echo 'PIXI_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
fi
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
fetch-tags: true
submodules: 'recursive'
- uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
with:
pixi-version: v0.42.1
cache: false
- name: Install the Apple certificate and provisioning profile
if: runner.os == 'macOS'
env:
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
DEVELOPER_TEAM_ID: ${{ secrets.DEVELOPER_TEAM_ID }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/FreeCAD_Weekly.provisionprofile
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
export KEYCHAIN_PASSWORD=$(openssl rand -base64 8)
# import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile
mkdir -p ~/Library/Provisioning\ Profiles
cp $PP_PATH ~/Library/Provisioning\ Profiles
xcrun notarytool store-credentials "FreeCAD" --keychain "$KEYCHAIN_PATH" --apple-id "${APPLE_ID}" --password "${APP_SPECIFIC_PASSWORD}" --team-id "${DEVELOPER_TEAM_ID}"
- name: Build and Release Packages
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGN_RELEASE: "true"
TARGET_PLATFORM: ${{ matrix.target }}
UPLOAD_RELEASE: "true"
run: |
cd package/rattler-build
pixi install
pixi run -e package create_bundle
## Needed if running on a self-hosted runner:
# - name: Clean up keychain and provisioning profile
# if: ${{ always() }}
# run: |
# security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
# rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision

View File

@@ -39,6 +39,9 @@ on:
type: string
required: true
permissions:
contents: read
jobs:
WrapUp:
@@ -50,11 +53,16 @@ jobs:
shell: bash
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit
- name: Make needed directories, files and initializations
run: |
mkdir -p ${{ env.artifactsDownloadDir }}
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
path: ${{ env.artifactsDownloadDir }}
- name: Save input data to file
@@ -111,7 +119,7 @@ jobs:
cat report.md >> $GITHUB_STEP_SUMMARY
- name: Delete used artifacts
continue-on-error: true
uses: geekyeggo/delete-artifact@v5
uses: geekyeggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b # v5.1.0
with:
name: |
${{ env.usedArtifacts }}

3
.gitmodules vendored
View File

@@ -7,3 +7,6 @@
[submodule "src/3rdParty/GSL"]
path = src/3rdParty/GSL
url = https://github.com/microsoft/GSL
[submodule "src/Mod/AddonManager"]
path = src/Mod/AddonManager
url = https://github.com/FreeCAD/AddonManager.git

View File

@@ -9,7 +9,6 @@ files: |
src/Main|
src/Tools|
tests/src|
src/Mod/AddonManager|
src/Mod/Assembly|
src/Mod/CAM|
src/Mod/Cloud|

View File

@@ -13,6 +13,18 @@
"CMAKE_EXPORT_COMPILE_COMMANDS": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_JOB_POOL_COMPILE": {
"type": "STRING",
"value": "compile_jobs"
},
"CMAKE_JOB_POOL_LINK": {
"type": "STRING",
"value": "link_jobs"
},
"CMAKE_POLICY_VERSION_MINIMUM": {
"type": "STRING",
"value": "3.5"
}
}
},
@@ -59,6 +71,10 @@
"type": "BOOL",
"value": "ON"
},
"BUILD_REVERSEENGINEERING": {
"type": "BOOL",
"value": "OFF"
},
"ENABLE_DEVELOPER_TESTS": {
"type": "BOOL",
"value": "ON"
@@ -87,6 +103,10 @@
"type": "BOOL",
"value": "ON"
},
"FREECAD_QT_VERSION": {
"type": "STRING",
"value": "6"
},
"OCCT_CMAKE_FALLBACK": {
"type": "BOOL",
"value": "ON"
@@ -107,26 +127,30 @@
},
"cmakeExecutable": "${sourceDir}/conda/cmake.sh",
"cacheVariables": {
"CMAKE_INCLUDE_PATH": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/include"
"CMAKE_C_COMPILER": {
"type": "STRING",
"value": "clang"
},
"CMAKE_INSTALL_LIBDIR": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/lib"
"CMAKE_CXX_COMPILER": {
"type": "STRING",
"value": "clang++"
},
"CMAKE_EXE_LINKER_FLAGS": {
"type": "STRING",
"value": "-fuse-ld=mold"
},
"CMAKE_INSTALL_PREFIX": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}"
},
"CMAKE_LIBRARY_PATH":{
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/lib"
},
"CMAKE_PREFIX_PATH": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}"
},
"CMAKE_SHARED_LINKER_FLAGS": {
"type": "STRING",
"value": "-fuse-ld=mold"
},
"OCC_INCLUDE_DIR": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/include/opencascade"
@@ -147,22 +171,10 @@
"type": "STRING",
"value": "/opt/homebrew;/usr/local/homebrew"
},
"CMAKE_INCLUDE_PATH": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/include"
},
"CMAKE_INSTALL_LIBDIR": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/lib"
},
"CMAKE_INSTALL_PREFIX": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}"
},
"CMAKE_LIBRARY_PATH":{
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/lib"
},
"CMAKE_PREFIX_PATH": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}"
@@ -183,22 +195,10 @@
},
"cmakeExecutable": "${sourceDir}/conda/cmake.cmd",
"cacheVariables": {
"CMAKE_INCLUDE_PATH": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/Library/include"
},
"CMAKE_INSTALL_LIBDIR": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/Library/lib"
},
"CMAKE_INSTALL_PREFIX": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/Library"
},
"CMAKE_LIBRARY_PATH":{
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/Library/lib"
},
"CMAKE_PREFIX_PATH": {
"type": "FILEPATH",
"value": "$env{CONDA_PREFIX}/Library"

View File

@@ -130,7 +130,7 @@ if (OCC_FOUND)
else ()
list(APPEND OCC_LIBRARIES TKDESTEP TKDEIGES TKDEGLTF TKDESTL)
endif ()
message(STATUS "-- Found OpenCASCADE version: ${OCC_VERSION_STRING}")
message(STATUS "-- OpenCASCADE include directory: ${OCC_INCLUDE_DIR}")
message(STATUS "-- OpenCASCADE shared libraries directory: ${OCC_LIBRARY_DIR}")
message(STATUS "Found OpenCASCADE version: ${OCC_VERSION_STRING}")
message(STATUS " OpenCASCADE include directory: ${OCC_INCLUDE_DIR}")
message(STATUS " OpenCASCADE shared libraries directory: ${OCC_LIBRARY_DIR}")
endif ()

View File

@@ -28,6 +28,7 @@ macro(CheckInterModuleDependencies)
REQUIRES_MODS(BUILD_MESH_PART BUILD_PART BUILD_MESH)
REQUIRES_MODS(BUILD_FLAT_MESH BUILD_MESH_PART)
REQUIRES_MODS(BUILD_OPENSCAD BUILD_MESH_PART BUILD_DRAFT)
REQUIRES_MODS(BUILD_MATERIAL_EXTERNAL BUILD_MATERIAL)
REQUIRES_MODS(BUILD_PART BUILD_MATERIAL)
REQUIRES_MODS(BUILD_PART_DESIGN BUILD_SKETCHER)
# REQUIRES_MODS(BUILD_CAM BUILD_PART BUILD_MESH BUILD_ROBOT)

View File

@@ -9,6 +9,13 @@ macro(CompilerChecksAndSetups)
# ================================================================================
# Use a heuristic of 1 GiB of RAM needed per compiler job and limit to
# a single link job. Modern linkers are multithreaded and running them concurrently
# can exhaust resources.
cmake_host_system_information(RESULT avail_mem_MiB QUERY TOTAL_PHYSICAL_MEMORY)
math(EXPR max_compile_procs "${avail_mem_MiB} / 1024")
set_property(GLOBAL PROPERTY JOB_POOLS compile_jobs=${max_compile_procs} link_jobs=1)
# Allow developers to use Boost < 1.74
if (NOT BOOST_MIN_VERSION)
set(BOOST_MIN_VERSION 1.74)

View File

@@ -10,6 +10,7 @@ macro(InitializeFreeCADBuildOptions)
option(FREECAD_USE_EXTERNAL_KDL "Use system installed orocos-kdl instead of the bundled." OFF)
option(FREECAD_USE_EXTERNAL_FMT "Use system installed fmt library if available instead of fetching the source." ON)
option(FREECAD_USE_EXTERNAL_ONDSELSOLVER "Use system installed OndselSolver instead of git submodule." OFF)
option(FREECAD_USE_EXTERNAL_E57FORMAT "Use system installed libE57Format instead of the bundled." OFF)
option(FREECAD_USE_FREETYPE "Builds the features using FreeType libs" ON)
option(FREECAD_BUILD_DEBIAN "Prepare for a build of a Debian package" OFF)
option(FREECAD_CHECK_PIVY "Check for pivy version using Python at build time" ON)
@@ -120,6 +121,7 @@ macro(InitializeFreeCADBuildOptions)
option(BUILD_INSPECTION "Build the FreeCAD inspection module" ON)
option(BUILD_JTREADER "Build the FreeCAD jt reader module" OFF)
option(BUILD_MATERIAL "Build the FreeCAD material module" ON)
option(BUILD_MATERIAL_EXTERNAL "Build the FreeCAD material external interface module" OFF)
option(BUILD_MESH "Build the FreeCAD mesh module" ON)
option(BUILD_MESH_PART "Build the FreeCAD mesh part module" ON)
option(BUILD_FLAT_MESH "Build the FreeCAD flat mesh module" ON)

View File

@@ -104,6 +104,7 @@ macro(PrintFinalReport)
value(BUILD_INSPECTION)
value(BUILD_JTREADER)
value(BUILD_MATERIAL)
value(BUILD_MATERIAL_EXTERNAL)
value(BUILD_MESH)
value(BUILD_MESH_PART)
value(BUILD_OPENSCAD)

View File

@@ -19,18 +19,29 @@ macro(SetGlobalCompilerAndLinkerSettings)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mxgot")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
# Only add -Og if no -O* optimization flag exists
if (NOT "${CMAKE_C_FLAGS_DEBUG}" MATCHES "-O[a-z0-9]+")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
endif()
if (NOT "${CMAKE_CXX_FLAGS_DEBUG}" MATCHES "-O[a-z0-9]+")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
endif()
endif()
if(MSVC)
# set default compiler settings
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR -DNOMINMAX)
add_compile_options(/Zm150 /bigobj)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFC_DEBUG")
# set default libs
set (CMAKE_C_STANDARD_LIBRARIES "kernel32.lib user32.lib gdi32.lib winspool.lib SHFolder.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib comsupp.lib Ws2_32.lib dbghelp.lib ")
set (CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}")
# set linker flag /nodefaultlib
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB")
if (MSVC_VERSION LESS 1930) # Anything before VS 2022
# set default libs
set (CMAKE_C_STANDARD_LIBRARIES "kernel32.lib user32.lib gdi32.lib winspool.lib SHFolder.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib comsupp.lib Ws2_32.lib dbghelp.lib ")
set (CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}")
# set linker flag /nodefaultlib
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB")
endif()
if(FREECAD_RELEASE_PDB)
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG")

View File

@@ -34,8 +34,8 @@ macro(SetupLibFmt)
cmake_policy(SET CMP0135 NEW)
endif()
FetchContent_Declare(fmt
URL https://github.com/fmtlib/fmt/archive/refs/tags/9.1.0.zip
URL_MD5 e6754011ff56bfc37631fcc90961e377
URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.zip
URL_MD5 90667b07f34d91554cf8285ae234ff66
)
FetchContent_MakeAvailable(fmt)
set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON)

View File

@@ -27,6 +27,12 @@ endif()
find_package(XercesC REQUIRED PATHS ${FREECAD_LIBPACK_DIR}/cmake NO_DEFAULT_PATH)
message(STATUS "Found LibPack 3 XercesC ${XercesC_VERSION}")
if(FREECAD_LIBPACK_VERSION VERSION_GREATER_EQUAL "3.1.1")
set(FREECAD_USE_EXTERNAL_E57FORMAT ON)
find_package(E57Format REQUIRED PATHS ${FREECAD_LIBPACK_DIR}/lib/cmake/e57format NO_DEFAULT_PATH)
message(STATUS "Found LibPack 3 e57format ${e57format_VERSION}")
endif()
find_package(yaml-cpp REQUIRED PATHS ${FREECAD_LIBPACK_DIR}/lib/cmake NO_DEFAULT_PATH)
message(STATUS "Found LibPack 3 yaml-cpp ${yaml-cpp_VERSION}")

View File

@@ -0,0 +1,37 @@
@echo on
@REM :: free up extra disk space, compare
@REM :: https://github.com/conda-forge/conda-smithy/issues/1949
@REM rmdir /s /q C:\hostedtoolcache\windows
@REM set "CFLAGS= "
@REM set "CXXFLAGS= -DBOOST_PROGRAM_OPTIONS_DYN_LINK=1"
@REM set "LDFLAGS_SHARED= ucrt.lib"
set "CMAKE_GENERATOR="
set "CMAKE_GENERATOR_PLATFORM="
cmake ^
--preset conda-windows-release ^
-D CMAKE_INCLUDE_PATH:FILEPATH="%LIBRARY_PREFIX%/include" ^
-D CMAKE_INSTALL_LIBDIR:FILEPATH="%LIBRARY_PREFIX%/lib" ^
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
-D CMAKE_LIBRARY_PATH:FILEPATH="%LIBRARY_PREFIX%/lib" ^
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
-D FREECAD_USE_OCC_VARIANT="Official Version" ^
-D INSTALL_TO_SITEPACKAGES:BOOL=ON ^
-D OCC_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%/include" ^
-D OCCT_CMAKE_FALLBACK:BOOL=OFF ^
-D Python_EXECUTABLE:FILEPATH="%PYTHON%" ^
-D Python3_EXECUTABLE:FILEPATH="%PYTHON%" ^
-B build ^
-S . ^
${CMAKE_PLATFORM_FLAGS[@]}
if %ERRORLEVEL% neq 0 exit 1
ninja -C build install
if %ERRORLEVEL% neq 0 exit 1
rmdir /s /q "%LIBRARY_PREFIX%\doc"
ren %LIBRARY_PREFIX%\bin\FreeCAD.exe freecad.exe
ren %LIBRARY_PREFIX%\bin\FreeCADCmd.exe freecadcmd.exe

View File

@@ -0,0 +1,47 @@
if [[ ${HOST} =~ .*linux.* ]]; then
CMAKE_PRESET=conda-linux-release
fi
if [[ ${HOST} =~ .*darwin.* ]]; then
CMAKE_PRESET=conda-macos-release
# add hacks for osx here!
echo "adding hacks for osx"
# install space-mouse
/usr/bin/curl -o /tmp/3dFW.dmg -L 'https://download.3dconnexion.com/drivers/mac/10-7-0_B564CC6A-6E81-42b0-82EC-418EA823B81A/3DxWareMac_v10-7-0_r3411.dmg'
hdiutil attach -readonly /tmp/3dFW.dmg
sudo installer -package /Volumes/3Dconnexion\ Software/Install\ 3Dconnexion\ software.pkg -target /
diskutil eject /Volumes/3Dconnexion\ Software
CMAKE_PLATFORM_FLAGS+=(-DFREECAD_USE_3DCONNEXION:BOOL=ON)
CMAKE_PLATFORM_FLAGS+=(-D3DCONNEXIONCLIENT_FRAMEWORK:FILEPATH="/Library/Frameworks/3DconnexionClient.framework")
CXXFLAGS="${CXXFLAGS} -D_LIBCPP_DISABLE_AVAILABILITY"
fi
unset CMAKE_GENERATOR
unset CMAKE_GENERATOR_PLATFORM
cmake \
--preset ${CMAKE_PRESET} \
-D CMAKE_IGNORE_PREFIX_PATH="/opt/homebrew;/usr/local/homebrew" \
-D CMAKE_INCLUDE_PATH:FILEPATH="$PREFIX/include" \
-D CMAKE_INSTALL_LIBDIR:FILEPATH="$PREFIX/lib" \
-D CMAKE_INSTALL_PREFIX:FILEPATH="$PREFIX" \
-D CMAKE_LIBRARY_PATH:FILEPATH="$PREFIX/lib" \
-D CMAKE_PREFIX_PATH:FILEPATH="$PREFIX" \
-D FREECAD_USE_OCC_VARIANT="Official Version" \
-D INSTALL_TO_SITEPACKAGES:BOOL=ON \
-D OCC_INCLUDE_DIR:FILEPATH="$PREFIX/include" \
-D OCCT_CMAKE_FALLBACK:BOOL=OFF \
-D Python_EXECUTABLE:FILEPATH="$PYTHON" \
-D Python3_EXECUTABLE:FILEPATH="$PYTHON" \
-B build \
-S . \
${CMAKE_PLATFORM_FLAGS[@]}
cmake --build build
cmake --install build
mv ${PREFIX}/bin/FreeCAD ${PREFIX}/bin/freecad || true
mv ${PREFIX}/bin/FreeCADCmd ${PREFIX}/bin/freecadcmd || true

View File

@@ -0,0 +1,7 @@
c_compiler: # [win]
- vs2022 # [win]
cxx_compiler: # [win]
- vs2022 # [win]
MACOSX_SDK_VERSION: # [osx]
- '11.0' # [osx]

View File

@@ -0,0 +1,31 @@
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export PREFIX=${HERE}/usr
# export LD_LIBRARY_PATH=${HERE}/usr/lib${LD_LIBRARY_PATH:+':'}$LD_LIBRARY_PATH
export PYTHONHOME=${HERE}/usr
export PATH_TO_FREECAD_LIBDIR=${HERE}/usr/lib
# export QT_QPA_PLATFORM_PLUGIN_PATH=${HERE}/usr/plugins
# export QT_XKB_CONFIG_ROOT=${HERE}/usr/lib
export FONTCONFIG_FILE=/etc/fonts/fonts.conf
export FONTCONFIG_PATH=/etc/fonts
# Show packages info if DEBUG env variable is set
if [ "$DEBUG" = 1 ]; then
cat ${HERE}/packages.txt
fi
# SSL
# https://forum.freecad.org/viewtopic.php?f=4&t=34873&start=20#p327416
export SSL_CERT_FILE=$PREFIX/ssl/cacert.pem
# https://github.com/FreeCAD/FreeCAD-AppImage/pull/20
export GIT_SSL_CAINFO=$HERE/usr/ssl/cacert.pem
# Support for launching other applications (from /usr/bin)
# https://github.com/FreeCAD/FreeCAD-AppImage/issues/30
if [ ! -z "$1" ] && [ -e "$HERE/usr/bin/$1" ] ; then
MAIN="$HERE/usr/bin/$1" ; shift
else
MAIN="$HERE/usr/bin/freecad"
fi
"${MAIN}" "$@"

View File

@@ -0,0 +1,84 @@
#!/bin/bash
set -e
set -x
conda_env="AppDir/usr"
mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
echo -e "\nDelete unnecessary stuff"
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
mv ${conda_env}/bin ${conda_env}/bin_tmp
mkdir ${conda_env}/bin
cp ${conda_env}/bin_tmp/freecad ${conda_env}/bin/
cp ${conda_env}/bin_tmp/freecadcmd ${conda_env}/bin
cp ${conda_env}/bin_tmp/ccx ${conda_env}/bin/
cp ${conda_env}/bin_tmp/python ${conda_env}/bin/
cp ${conda_env}/bin_tmp/pip ${conda_env}/bin/
cp ${conda_env}/bin_tmp/pyside6-rcc ${conda_env}/bin/
cp ${conda_env}/bin_tmp/gmsh ${conda_env}/bin/
cp ${conda_env}/bin_tmp/dot ${conda_env}/bin/
cp ${conda_env}/bin_tmp/unflatten ${conda_env}/bin/
rm -rf ${conda_env}/bin_tmp
sed -i '1s|.*|#!/usr/bin/env python|' ${conda_env}/bin/pip
echo -e "\nCopying Icon and Desktop file"
cp ${conda_env}/share/applications/org.freecad.FreeCAD.desktop AppDir/
sed -i 's/Exec=FreeCAD/Exec=AppRun/g' AppDir/org.freecad.FreeCAD.desktop
cp ${conda_env}/share/icons/hicolor/scalable/apps/org.freecad.FreeCAD.svg AppDir/
# Remove __pycache__ folders and .pyc files
find . -path "*/__pycache__/*" -delete
find . -name "*.pyc" -type f -delete
# reduce size
rm -rf ${conda_env}/conda-meta/
rm -rf ${conda_env}/doc/global/
rm -rf ${conda_env}/share/gtk-doc/
rm -rf ${conda_env}/lib/cmake/
find . -name "*.h" -type f -delete
find . -name "*.cmake" -type f -delete
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-Linux-$(uname -m)-${python_version}"
echo -e "\################"
echo -e "version_name: ${version_name}"
echo -e "################"
pixi list -e default > AppDir/packages.txt
sed -i "1s/.*/\nLIST OF PACKAGES:/" AppDir/packages.txt
export tag="weekly-builds" # should retrieve from git tag
curl -LO https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage
chmod a+x appimagetool-$(uname -m).AppImage
echo -e "\nCreate the appimage"
# export GPG_TTY=$(tty)
chmod a+x ./AppDir/AppRun
./appimagetool-$(uname -m).AppImage \
--comp zstd \
--mksquashfs-opt -Xcompression-level \
--mksquashfs-opt 22 \
-u "gh-releases-zsync|FreeCAD|FreeCAD-Bundle|$tag|FreeCAD*$(uname -m)*.AppImage.zsync" \
AppDir ${version_name}.AppImage
# -s --sign-key ${GPG_KEY_ID} \
echo -e "\nCreate hash"
sha256sum ${version_name}.AppImage > ${version_name}.AppImage-SHA256.txt
if [ "${UPLOAD_RELEASE}" == "true" ]; then
gh release create ${BUILD_TAG} --title "Weekly Build ${BUILD_TAG}" --notes "Weekly Build ${BUILD_TAG}" --prerelease || true
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage-SHA256.txt"
fi

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>FreeCAD</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string>freecad.icns</string>
<key>CFBundleIdentifier</key>
<string>org.freecad.FreeCAD</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string></string>
<key>CFBundleName</key>
<string>APPLICATION_MENU_NAME</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string></string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>FREECAD_VERSION</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>NSRequiresAquaSystemAppearance</key>
<string>False</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>FCStd</string>
<string>FCMat</string>
<string>FCParam</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>freecad-doc.icns</string>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>FCMacro</string>
<string>FCScript</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>freecad-script.icns</string>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>FreeCAD Document</string>
<key>UTTypeIdentifier</key>
<string>org.freecad.fcstd</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>FCStd</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,85 @@
#!/bin/bash
set -e
set -x
conda_env="FreeCAD.app/Contents/Resources"
mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
# delete unnecessary stuff
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
mv ${conda_env}/bin ${conda_env}/bin_tmp
mkdir ${conda_env}/bin
cp ${conda_env}/bin_tmp/freecad ${conda_env}/bin/
cp ${conda_env}/bin_tmp/freecadcmd ${conda_env}/bin
cp ${conda_env}/bin_tmp/ccx ${conda_env}/bin/
cp ${conda_env}/bin_tmp/python ${conda_env}/bin/
cp ${conda_env}/bin_tmp/pip ${conda_env}/bin/
cp ${conda_env}/bin_tmp/pyside6-rcc ${conda_env}/bin/
cp ${conda_env}/bin_tmp/gmsh ${conda_env}/bin/
cp ${conda_env}/bin_tmp/dot ${conda_env}/bin/
cp ${conda_env}/bin_tmp/unflatten ${conda_env}/bin/
rm -rf ${conda_env}/bin_tmp
sed -i '1s|.*|#!/usr/bin/env python|' ${conda_env}/bin/pip
# copy resources
cp resources/* ${conda_env}
# Remove __pycache__ folders and .pyc files
find . -path "*/__pycache__/*" -delete
find . -name "*.pyc" -type f -delete
# fix problematic rpaths and reexport_dylibs for signing
# see https://github.com/FreeCAD/FreeCAD/issues/10144#issuecomment-1836686775
# and https://github.com/FreeCAD/FreeCAD-Bundle/pull/203
python ../scripts/fix_macos_lib_paths.py ${conda_env}/lib
# build and install the launcher
cmake -B build launcher
cmake --build build
mkdir -p FreeCAD.app/Contents/MacOS
cp build/FreeCAD FreeCAD.app/Contents/MacOS/FreeCAD
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-macOS-$(uname -m)-${python_version}"
application_menu_name="FreeCAD_${BUILD_TAG}"
echo -e "\################"
echo -e "version_name: ${version_name}"
echo -e "################"
cp Info.plist.template ${conda_env}/../Info.plist
sed -i "s/FREECAD_VERSION/${version_name}/" ${conda_env}/../Info.plist
sed -i "s/APPLICATION_MENU_NAME/${application_menu_name}/" ${conda_env}/../Info.plist
pixi list -e default > FreeCAD.app/Contents/packages.txt
sed -i '1s/.*/\nLIST OF PACKAGES:/' FreeCAD.app/Contents/packages.txt
# copy the plugin into its final location
cp -a ${conda_env}/Library ${conda_env}/..
rm -rf ${conda_env}/Library
if [[ "${SIGN_RELEASE}" == "true" ]]; then
# create the signed dmg
./macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
else
# create the dmg
dmgbuild -s dmg_settings.py "FreeCAD" "${version_name}.dmg"
fi
# create hash
sha256sum ${version_name}.dmg > ${version_name}.dmg-SHA256.txt
if [[ "${UPLOAD_RELEASE}" == "true" ]]; then
gh release create ${BUILD_TAG} --title "Weekly Build ${BUILD_TAG}" --notes "Weekly Build ${BUILD_TAG}" --prerelease || true
gh release upload --clobber ${BUILD_TAG} "${version_name}.dmg" "${version_name}.dmg-SHA256.txt"
fi

View File

@@ -0,0 +1,12 @@
# Ensure default values are set in defines if they are not already provided
defines.setdefault('containing_folder', '.')
defines.setdefault('app_name', 'FreeCAD.app')
defines.setdefault('icon_path', 'Contents/Resources/freecad.icns')
files = [f"{defines['containing_folder']}/{defines['app_name']}"]
symlinks = {"Applications": "/Applications"}
badge_icon = f"{defines['containing_folder']}/{defines['app_name']}/{defines['icon_path']}"
window_rect = ((200, 200), (600, 400))
icon_locations = {f"{defines['app_name']}": (180, 150), "Applications": (420, 150)}
size = "4g"

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.20)
project(freecad-launcher LANGUAGES CXX)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version" FORCE)
set(CMAKE_CXX_STANDARD 17)
add_executable(FreeCAD FreeCAD.cpp)
target_link_libraries(FreeCAD)

View File

@@ -0,0 +1,75 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <vector>
#include <libgen.h>
#include <unistd.h>
int main(int argc, char *argv[], char *const *envp) {
char *cwd = dirname(realpath(argv[0], NULL));
std::string FreeCAD = realpath((std::string(cwd) + "/../Resources/bin/freecad").c_str(), NULL);
std::map<std::string, std::string> env;
for(int i = 0; envp[i] != NULL; ++i) {
std::string e(envp[i]);
auto sep = e.find('=');
auto var = e.substr(0, sep);
auto value = e.substr(sep+1, std::string::npos);
env[var] = value;
}
std::string prefix = realpath((std::string(cwd) + "/../Resources").c_str(), NULL);
env["PREFIX"] = prefix;
env["LD_LIBRARY_PATH"] = prefix + "/lib";
env["PYTHONPATH"] = prefix;
env["PYTHONHOME"] = prefix;
env["FONTCONFIG_FILE"] = "/etc/fonts/fonts.conf";
env["FONTCONFIG_PATH"] = "/etc/fonts";
env["LANG"] = "UTF-8"; // https://forum.freecad.org/viewtopic.php?f=22&t=42644
env["SSL_CERT_FILE"] = prefix + "/ssl/cacert.pem"; // https://forum.freecad.org/viewtopic.php?f=3&t=42825
env["GIT_SSL_CAINFO"] = prefix + "/ssl/cacert.pem";
env["QT_MAC_WANTS_LAYER"] = "1";
char **new_env = new char*[env.size() + 1];
int i = 0;
for (const auto& [var, value] : env) {
auto line = var + '=' + value;
size_t len = line.length() + 1;
new_env[i] = new char[len];
memset(new_env[i], 0, len);
strncpy(new_env[i], line.c_str(), len);
i++;
}
new_env[i] = NULL;
i = 0;
while(new_env[i] != NULL) {
std::cout << new_env[i] << std::endl;
i++;
}
std::cout << "Running: " << FreeCAD << std::endl;
i = 0;
while(argv[i] != NULL) {
i++;
}
char **new_argv = new char*[i + 1];
new_argv[0] = new char[FreeCAD.length() + 1];
memset(new_argv[0], 0, FreeCAD.length() + 1);
strncpy(new_argv[0], FreeCAD.c_str(), FreeCAD.length());
i = 1;
while(argv[i] != NULL) {
new_argv[i] = new char[strlen(argv[i])+1];
memset(new_argv[i], 0, strlen(argv[i])+1);
strncpy(new_argv[i], argv[i], strlen(argv[i]));
i++;
}
new_argv[i] = NULL;
return execve(FreeCAD.c_str(), new_argv, new_env);
}

View File

@@ -0,0 +1,126 @@
#!/bin/zsh
# SPDX-License-Identifier: LGPL-2.1-or-later
# Default values
SIGNING_KEY_ID="${FREECAD_SIGNING_KEY_ID}"
KEYCHAIN_PROFILE="FreeCAD"
CONTAINING_FOLDER="."
APP_NAME="FreeCAD.app"
VOLUME_NAME="FreeCAD"
DMG_NAME="FreeCAD-macOS-$(uname -m).dmg"
DMG_SETTINGS="dmg_settings.py"
# Function to display usage information
function usage {
echo "Usage: $0 [-k|--key-id <signing_key_id>] [-p|--keychain-profile <keychain_profile>]"
echo " [-d|--dir <containing_folder>] [-n|--app-name <app_name.app>]"
echo " [-v|--volume-name <volume_name>] [-o|--output <image_name.dmg>]"
echo " [-s|--dmg-settings <dmg_settings.py>]"
echo
echo "This script signs and notarizes a FreeCAD.app bundle. It expects that the bundle is in a folder"
echo "by itself (that folder will be used as the basis for the created disk image file, so anything"
echo "else in it will become part of the image). That folder should be located in the same folder as"
echo "this script."
echo
echo "If <signing_key_id> is not passed it defaults to env variable FREECAD_SIGNING_KEY_ID, it should"
echo "be a Developer ID Application certificate that has been installed into the login keychain."
echo "For a list of available keys see the output of"
echo " security find-identity -p basic -v"
echo "For instructions on how to configure the credentials for the tool for use before running this"
echo "script see the documentation for"
echo " xcrun notarytool store-credentials"
exit 1
}
# Parse command line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
-k|--key-id)
SIGNING_KEY_ID="$2"
shift 2
;;
-p|--keychain-profile)
KEYCHAIN_PROFILE="$2"
shift 2
;;
-d|--dir)
CONTAINING_FOLDER="$2"
shift 2
;;
-n|--app-name)
APP_NAME="$2"
shift 2
;;
-v|--volume-name)
VOLUME_NAME="$2"
shift 2
;;
-o|--output)
DMG_NAME="$2"
shift 2
;;
-s|--dmg-settings)
DMG_SETTINGS="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown parameter passed: $1"
usage
;;
esac
done
# Check if SIGNING_KEY_ID is set
if [ -z "$SIGNING_KEY_ID" ]; then
echo "Error: Signing key ID is required."
usage
fi
# Check for dmgbuild executable
if ! command -v dmgbuild &> /dev/null; then
echo "Error: dmgbuild not installed. Please install it for example using pip:"
echo 'pip3 install "dmgbuild[badge_icons]>=1.6.0,<1.7.0"'
exit 1
fi
function run_codesign {
echo "Signing $1"
/usr/bin/codesign --options runtime -f -s ${SIGNING_KEY_ID} --timestamp --entitlements entitlements.plist "$1"
}
IFS=$'\n'
dylibs=($(/usr/bin/find "${CONTAINING_FOLDER}/${APP_NAME}" -name "*.dylib"))
shared_objects=($(/usr/bin/find "${CONTAINING_FOLDER}/${APP_NAME}" -name "*.so"))
bundles=($(/usr/bin/find "${CONTAINING_FOLDER}/${APP_NAME}" -name "*.bundle"))
executables=($(/usr/bin/find "${CONTAINING_FOLDER}/${APP_NAME}" -type f -perm +111 -exec file {} + | grep "Mach-O 64-bit executable" | sed 's/:.*//g'))
IFS=$' \t\n' # The default
signed_files=("${dylibs[@]}" "${shared_objects[@]}" "${bundles[@]}" "${executables[@]}")
# This list of files is generated from:
# file `find . -type f -perm +111 -print` | grep "Mach-O 64-bit executable" | sed 's/:.*//g'
for exe in ${signed_files}; do
run_codesign "${exe}"
done
# Two additional files that must be signed that aren't caught by the above searches:
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/packages.txt"
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd"
# Finally, sign the app itself (must be done last)
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}"
# Create a disk image from the folder
echo "Creating disk image ${DMG_NAME}"
dmgbuild -s ${DMG_SETTINGS} -Dcontaining_folder="${CONTAINING_FOLDER}" -Dapp_name="${APP_NAME}" "${VOLUME_NAME}" "${DMG_NAME}"
# Submit it for notarization (requires that an App Store API Key has been set up in the notarytool)
time xcrun notarytool submit --wait --keychain-profile "${KEYCHAIN_PROFILE}" "${DMG_NAME}"
# Assuming that notarization succeeded, it's a good practice to staple that notarization to the DMG
xcrun stapler staple "${DMG_NAME}"

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
[workspace]
channels = [
"https://prefix.dev/pixi-build-backends",
"https://prefix.dev/conda-forge",
]
platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64", "win-64"]
preview = ["pixi-build"]
[package]
name = "freecad"
version = "1.1.0dev"
homepage = "https://freecad.org"
repository = "https://github.com/FreeCAD/FreeCAD"
description = "FreeCAD"
[package.build]
backend = { name = "pixi-build-rattler-build", version = "*" }
[feature.freecad.dependencies]
freecad = { path = "." }
[feature.package.dependencies]
python = ">=3.11,<3.12"
## Linux (x86-64)
[feature.package.target.linux-64.dependencies]
coreutils = "*"
[feature.package.target.linux-64.tasks]
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
## Linux (aarch64)
[feature.package.target.linux-aarch64.dependencies]
coreutils = "*"
[feature.package.target.linux-aarch64.tasks]
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
## macOS (Intel)
[feature.package.target.osx-64.dependencies]
coreutils = "*"
dmgbuild = "*"
sed = "*"
[feature.package.target.osx-64.tasks]
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
## macOS (Apple Silicon)
[feature.package.target.osx-arm64.dependencies]
coreutils = "*"
dmgbuild = "*"
sed = "*"
[feature.package.target.osx-arm64.tasks]
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
## Windows dependencies (x86-64)
[feature.package.target.win-64.dependencies]
git = "*"
[feature.package.target.win-64.tasks]
create_bundle = 'bash -c "cd windows && bash create_bundle.sh"'
[environments]
default = ["freecad"]
package = ["package"]

View File

@@ -0,0 +1,176 @@
context:
version: "1.1.0dev"
package:
name: freecad
version: "${{ version }}"
source:
path: ../..
use_gitignore: true
build:
number: 0
requirements:
build:
- ccache
- cmake
- compilers
- doxygen
- ninja
- noqt5
- python>=3.11,<3.12
- qt6-main<6.9
- swig
- if: linux and x86_64
then:
- clang
- clangxx
- kernel-headers_linux-64
- libdrm-cos7-x86_64
- libselinux-cos7-x86_64
- libsepol-cos7-x86_64
- libspnav
- libx11-common-cos7-x86_64
- libx11-cos7-x86_64
- libxau-cos7-x86_64
- libxcb-cos7-x86_64
- libxdamage-cos7-x86_64
- libxext-cos7-x86_64
- libxfixes-cos7-x86_64
- libxi-cos7-x86_64
- libxi-devel-cos7-x86_64
- libxxf86vm-cos7-x86_64
- mesa-dri-drivers-cos7-x86_64
- mesa-libegl-cos7-x86_64
- mesa-libegl-devel-cos7-x86_64
- mesa-libgl-cos7-x86_64
- mesa-libgl-devel-cos7-x86_64
- mold
- pixman-cos7-x86_64
- sed
- sysroot_linux-64
- xorg-x11-server-common-cos7-x86_64
- xorg-x11-server-xvfb-cos7-x86_64
- xorg-xproto
- if: linux and aarch64
then:
- clang
- clangxx
- kernel-headers_linux-aarch64
- libdrm-cos7-aarch64
- libselinux-cos7-aarch64
- libsepol-cos7-aarch64
- libspnav
- libx11-common-cos7-aarch64
- libx11-cos7-aarch64
- libxau-cos7-aarch64
- libxcb-cos7-aarch64
- libxdamage-cos7-aarch64
- libxext-cos7-aarch64
- libxfixes-cos7-aarch64
- libxi-cos7-aarch64
- libxi-devel-cos7-aarch64
- libxxf86vm-cos7-aarch64
- mesa-dri-drivers-cos7-aarch64
- mesa-libegl-cos7-aarch64
- mesa-libegl-devel-cos7-aarch64
- mesa-libgl-cos7-aarch64
- mesa-libgl-devel-cos7-aarch64
- mold
- pixman-cos7-aarch64
- sed
- sysroot_linux-aarch64
- xorg-x11-server-common-cos7-aarch64
- xorg-x11-server-xvfb-cos7-aarch64
- xorg-xproto
- if: build_platform != target_platform
then:
- cross-python_${{ target_platform }}
host:
- coin3d
- eigen
- fmt
- freetype
- hdf5
- libboost-devel
- matplotlib-base
- noqt5
- numpy
- occt
- pcl
- pivy
- ply
- pybind11
- pyside6
- python>=3.11,<3.12
- qt6-main<6.9
- six
- smesh
- vtk
- xerces-c
- yaml-cpp
- zlib
- if: win
then:
- winpthreads-devel
- tbb-devel
- if: unix
then:
- sed
- if: linux
then:
- libspnav
- xorg-xproto
- if: osx
then:
- blas * accelerate*
run:
- blas
- blinker
- calculix
- debugpy
- docutils
- gmsh
- graphviz
- ifcopenshell
- jinja2
- lark
- lxml
- matplotlib-base
- nine
- noqt5
- numpy
- occt
- olefile
- opencamlib
- opencv
- pandas
- pip
- pivy
- ply
- pycollada
- pyside6
- python>=3.11,<3.12
- pythonocc-core
- pyyaml
- qt6-main<6.9
- requests
- scipy
- sympy
- vtk
- xlutils
- if: linux
then:
- libspnav

View File

@@ -0,0 +1,13 @@
diff --git a/src/Tools/SubWCRev.py b/src/Tools/SubWCRev.py
index 1f3f0a436343..c314003736f1 100644
--- a/src/Tools/SubWCRev.py
+++ b/src/Tools/SubWCRev.py
@@ -523,7 +523,7 @@ def main():
inp = open("%s/src/Build/Version.h.in" % (bindir))
lines = inp.readlines()
inp.close()
- lines = i.writeVersion(lines)
+ #lines = i.writeVersion(lines)
out = open("%s/src/Build/Version.h.out" % (bindir), "w")
out.writelines(lines)
out.write("\n")

View File

@@ -0,0 +1,88 @@
import os
import subprocess
import re
import sys
if len(sys.argv) < 1 or "-h" in sys.argv:
print("""Usage: python fix_macos_paths.py <scan_path> [-r] [-s]
Options:
-r scan the directory recursively
-s scan only without fixing absolute paths in LC_RPATH or LC_REEXPORT_DYLIB
""")
sys.exit(1)
scan_path = os.path.abspath(os.path.expanduser(sys.argv[1]))
recursive = "-r" in sys.argv
scanmode = "-s" in sys.argv
def get_lc_paths(output):
if "is not an object file" in output:
return [], []
rpath_result = []
reexport_result = []
matches = re.finditer(r'cmd LC_RPATH', output)
for match in matches:
pos = match.start(0)
path_match = re.search(r'path (.*) \(offset.+?\)', output[pos:])
rpath_result.append(path_match.group(1))
matches = re.finditer(r'cmd LC_REEXPORT_DYLIB', output)
for match in matches:
pos = match.start(0)
path_match = re.search(r'name (.*) \(offset.+?\)', output[pos:])
reexport_result.append(path_match.group(1))
return rpath_result, reexport_result
def remove_rpath(file_path, rpath):
subprocess.run(['install_name_tool', '-delete_rpath', rpath, file_path])
subprocess.run(['codesign', '--force', '--sign', '-', file_path])
print(f'\nRemoved rpath {rpath} from {file_path}')
def change_reexport_dylib(file_path, reexport_dylib):
rel_reexport_dylib = "@rpath/" + os.path.basename(reexport_dylib)
subprocess.run(['install_name_tool', '-change', reexport_dylib, rel_reexport_dylib, file_path])
subprocess.run(['codesign', '--force', '--sign', '-', file_path])
print(f'\nChanged reexport dylib {reexport_dylib} to {rel_reexport_dylib} in {file_path}')
def scan_directory(directory, recursive=False):
if recursive:
print(f"Recursively scanning dir: {scan_path}")
else:
print(f"Scanning dir: {scan_path}")
for filename in os.listdir(directory):
full_path = os.path.join(directory, filename)
if recursive and os.path.isdir(full_path):
scan_directory(full_path, recursive)
continue
elif not os.path.isfile(full_path) or os.path.islink(full_path):
continue
try:
output = subprocess.check_output(['otool', '-l', full_path], text=True)
rpaths, reexport_dylibs = get_lc_paths(output)
except:
continue
file_dir = os.path.dirname(full_path)
for rpath in rpaths:
if os.path.isabs(rpath) and os.path.samefile(file_dir, rpath):
if scanmode:
print(f'\nFound absolute path in LC_RPATH: {rpath}\nIn: {full_path}')
else:
remove_rpath(full_path, rpath)
for reexport_dylib in reexport_dylibs:
if os.path.isabs(reexport_dylib):
if scanmode:
print(f'\nFound absolute path inLC_REEXPORT_DYLIB: {reexport_dylib}\nIn: {full_path}')
else:
change_reexport_dylib(full_path, reexport_dylib)
scan_directory(scan_path, recursive)
print("Done")

View File

@@ -0,0 +1,53 @@
import sys
import os
import subprocess
import platform
from datetime import datetime
import freecad
import FreeCAD
package_manager = "conda"
system = platform.platform().split("-")[0]
arch = platform.machine()
# Windows uses a different syntax
if arch == "AMD64":
arch = "x86_64"
if "ARCH" in os.environ:
if os.environ["ARCH"] != "":
arch = os.environ["ARCH"]
python_version = platform.python_version().split(".")
python_version = "py" + python_version[0] + python_version[1]
date = str(datetime.now()).split(" ")[0]
version_info = FreeCAD.Version()
build_version_suffix = FreeCAD.ConfigGet("BuildVersionSuffix")
dev_version = version_info[0] + "." + version_info[1] + "." + version_info[2] + build_version_suffix
revision = version_info[3].split(" ")[0]
if system == "macOS":
import jinja2
print("create plist from template")
osx_directory = os.path.join(os.path.dirname(__file__), "..", "osx")
with open(os.path.join(osx_directory, "Info.plist.template")) as template_file:
template_str = template_file.read()
template = jinja2.Template(template_str)
rendered_str = template.render( FREECAD_VERSION="{}-{}".format(dev_version, revision),
APPLICATION_MENU_NAME="FreeCAD-{}-{}".format(dev_version, revision) )
with open(os.path.join(osx_directory, "FreeCAD.app", "Contents", "Info.plist"), "w") as rendered_file:
rendered_file.write(rendered_str)
if "DEPLOY_RELEASE" in os.environ and os.environ["DEPLOY_RELEASE"] == "weekly-builds":
dev_version = "weekly-builds"
revision_separator = "-"
else:
revision_separator = ""
revision = ""
bundle_name = f"FreeCAD_{dev_version}{revision_separator}{revision}-{package_manager}-{system}-{arch}-{python_version}"
with open("bundle_name.txt", "w") as bundle_name_file:
bundle_name_file.write(bundle_name)

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
# call this file from within the FreeCAD git repo
# this script creates a file with the important version information
import os
import sys
import subprocess
sys.path.append(f"{os.getcwd()}/src/Tools")
import SubWCRev
gitInfo = SubWCRev.GitControl()
gitInfo.extractInfo("","")
i = open("src/Build/Version.h.cmake")
content = []
for line in i.readlines():
line = line.replace("${PACKAGE_WCREF}",gitInfo.rev)
line = line.replace("${PACKAGE_WCDATE}",gitInfo.date)
line = line.replace("${PACKAGE_WCURL}",gitInfo.url)
content.append(line)
with open("src/Build/Version.h.cmake", "w") as o:
content.append('// Git relevant stuff\n')
content.append('#define FCRepositoryHash "%s"\n' % (gitInfo.hash))
content.append('#define FCRepositoryBranch "%s"\n' % (gitInfo.branch))
o.writelines(content)
with open(os.sys.argv[1], "w") as f:
f.write(f"rev_number: {gitInfo.rev}\n")
f.write(f"branch_name: {gitInfo.branch}\n")
f.write(f"commit_date: {gitInfo.date}\n")
f.write(f"commit_hash: {gitInfo.hash}\n")
f.write(f"remote_url: {gitInfo.url}\n")
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c", "user.email='41898282+github-actions[bot]@users.noreply.github.com'",
"commit", "-a", "-m", "add git information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print(out.decode())
print(err.decode())

View File

@@ -0,0 +1,56 @@
set conda_env="fc_env"
robocopy ..\.pixi\envs\default\* %conda_env% /S /MT:%NUMBER_OF_PROCESSORS% > nul
%conda_env%\python ..\scripts\get_freecad_version.py
set /p freecad_version_name= <bundle_name.txt
echo **********************
echo %freecad_version_name%
echo **********************
REM remove arm binaries that fail to extract unless using latest 7zip
for /r %conda_env% %%i in (*arm*.exe) do (@echo "%%i will be removed" & @del "%%i")
set copy_dir="FreeCAD_Conda_Build"
mkdir %copy_dir%
REM Copy Conda's Python and (U)CRT to FreeCAD/bin
robocopy %conda_env%\DLLs %copy_dir%\bin\DLLs /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Lib %copy_dir%\bin\Lib /XD __pycache__ /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Scripts %copy_dir%\bin\Scripts /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\python*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\msvc*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\ucrt*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy meaningful executables
robocopy %conda_env%\Library\bin %copy_dir%\bin\ ccx.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ gmsh.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ dot.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ unflatten.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\mingw-w64\bin * %copy_dir%\bin\ /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy Conda's QT5/plugins to FreeCAD/bin
robocopy %conda_env%\Library\plugins %copy_dir%\bin\ /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\resources %copy_dir%\resources /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\translations %copy_dir%\translations /MT:%NUMBER_OF_PROCESSORS% > nul
REM get all the dependency .dlls
robocopy %conda_env%\Library\bin *.dll %copy_dir%\bin /XF *.pdb /XF api*.* /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy FreeCAD build
robocopy %conda_env%\Library\bin FreeCAD* %copy_dir%\bin /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\data %copy_dir%\data /XF *.txt /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\Ext %copy_dir%\Ext /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\lib %copy_dir%\lib /XF *.lib /XF *.prl /XF *.sh /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\Mod %copy_dir%\Mod /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\doc %copy_dir%\doc ThirdPartyLibraries.html LICENSE.html /MT:%NUMBER_OF_PROCESSORS% > nul
REM Apply Patches
rename %copy_dir%\bin\Lib\ssl.py ssl-orig.py
copy ssl-patch.py %copy_dir%\bin\Lib\ssl.py
cd %copy_dir%\..
ren %copy_dir% %freecad_version_name%
dir
REM if errorlevel1 exit 1
"%ProgramFiles%\7-Zip\7z.exe" a -t7z -mx9 -mmt=%NUMBER_OF_PROCESSORS% %freecad_version_name%.7z %freecad_version_name%\ -bb
certutil -hashfile "%freecad_version_name%.7z" SHA256 > "%freecad_version_name%.7z"-SHA256.txt
echo %date%-%time% >>"%freecad_version_name%.7z"-SHA256.txt

View File

@@ -0,0 +1,78 @@
#!/bin/bash
set -e
set -x
conda_env="fc_env"
mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
# remove arm binaries that fail to extract unless using latest 7zip
rm $(find ${conda_env} -name \*arm\*.exe)
# delete unnecessary stuff
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
copy_dir="FreeCAD_Conda_Build"
mkdir -p ${copy_dir}/bin
# Copy Conda's Python and (U)CRT to FreeCAD/bin
cp -a ${conda_env}/DLLs ${copy_dir}/bin/DLLs
cp -a ${conda_env}/Lib ${copy_dir}/bin/Lib
cp -a ${conda_env}/Scripts ${copy_dir}/bin/Scripts
cp -a ${conda_env}/python*.* ${copy_dir}/bin
cp -a ${conda_env}/msvc*.* ${copy_dir}/bin
cp -a ${conda_env}/ucrt*.* ${copy_dir}/bin
# Copy meaningful executables
cp -a ${conda_env}/Library/bin/ccx.exe ${copy_dir}/bin
cp -a ${conda_env}/Library/bin/gmsh.exe ${copy_dir}/bin
cp -a ${conda_env}/Library/bin/dot.exe ${copy_dir}/bin
cp -a ${conda_env}/Library/bin/unflatten.exe ${copy_dir}/bin
cp -a ${conda_env}/Library/mingw-w64/bin/* ${copy_dir}/bin
# copy resources -- perhaps needs reduction
cp -a ${conda_env}/Library/share ${copy_dir}/share
# get all the dependency .dlls
cp -a ${conda_env}/Library/bin/*.dll ${copy_dir}/bin
# Copy FreeCAD build
cp -a ${conda_env}/Library/bin/freecad* ${copy_dir}/bin
cp -a ${conda_env}/Library/bin/FreeCAD* ${copy_dir}/bin
cp -a ${conda_env}/Library/data ${copy_dir}/data
cp -a ${conda_env}/Library/Ext ${copy_dir}/Ext
cp -a ${conda_env}/Library/lib ${copy_dir}/lib
cp -a ${conda_env}/Library/Mod ${copy_dir}/Mod
rm -rf ${conda_env}/bin_tmp
# Apply Patches
mv ${copy_dir}/bin/Lib/ssl.py ssl-orig.py
cp ssl-patch.py ${copy_dir}/bin/Lib/ssl.py
echo '[Paths]' >> ${copy_dir}/bin/qt6.conf
echo 'Prefix = ../lib/qt6' >> ${copy_dir}/bin/qt6.conf
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-Windows-$(uname -m)-${python_version}"
echo -e "################"
echo -e "version_name: ${version_name}"
echo -e "################"
pixi list -e default > ${copy_dir}/packages.txt
sed -i '1s/.*/\nLIST OF PACKAGES:/' ${copy_dir}/packages.txt
mv ${copy_dir} ${version_name}
"${PROGRAMFILES}/7-Zip/7z.exe" a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
# create hash
sha256sum ${version_name}.7z > ${version_name}.7z-SHA256.txt
if [ "${UPLOAD_RELEASE}" == "true" ]; then
gh release create ${BUILD_TAG} --title "Weekly Build ${BUILD_TAG}" --notes "Weekly Build ${BUILD_TAG}" --prerelease || true
gh release upload --clobber ${BUILD_TAG} "${version_name}.7z" "${version_name}.7z-SHA256.txt"
fi

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@ fi
packages=(
ccache
cmake
doxygen
graphviz
imagemagick

17484
pixi.lock

File diff suppressed because it is too large Load Diff

View File

@@ -7,12 +7,15 @@ channels = ["conda-forge"]
platforms = [ "linux-64", "linux-aarch64", "osx-64", "osx-arm64", "win-64" ]
[dependencies]
blinker = "*"
calculix = "*"
ccache = "*"
cmake = "3.28.3"
cmake = "*"
coin3d = "*"
compilers = "*"
conda-devenv = "*"
debugpy = "*"
docutils = "*"
doxygen = "*"
eigen = "*"
fmt = "*"
@@ -20,35 +23,50 @@ freetype = "*"
git = "*"
graphviz = "*"
hdf5 = "*"
ifcopenshell = "*"
lark = "*"
libboost-devel = "*"
lxml = "*"
matplotlib = "*"
nine = "*"
ninja = "*"
noqt5 = "*"
numpy = "*"
occt = "*"
opencamlib = "*"
opencv = "*"
openssl = "*"
pandas = "*"
pcl = "*"
pip = "*"
pivy = "*"
ply = "*"
pre-commit = "*"
pybind11 = "*"
pycollada = "*"
pyside6 = "*"
python = "3.13"
python = ">=3.11,<3.12"
pythonocc-core = "*"
pyyaml = "*"
qt6-main = "*"
scipy = ">=1.14.1,<2"
requests = "*"
scipy = "*"
six = "*"
smesh = "*"
swig = "*"
sympy = "*"
tbb-devel = "*"
vtk = "*"
xerces-c = "*"
xlutils = "*"
yaml-cpp = "*"
zlib = "*"
zstd = "*"
## Linux Dependencies (x86-64)
[target.linux-64.dependencies]
clang = "*"
clangxx = "*"
kernel-headers_linux-64 = "*"
libdrm-cos7-x86_64 = "*"
libselinux-cos7-x86_64 = "*"
@@ -64,11 +82,13 @@ libxfixes-cos7-x86_64 = "*"
libxi-cos7-x86_64 = "*"
libxi-devel-cos7-x86_64 = "*"
libxxf86vm-cos7-x86_64 = "*"
llvm-openmp = "*"
mesa-dri-drivers-cos7-x86_64 = "*"
mesa-libegl-cos7-x86_64 = "*"
mesa-libegl-devel-cos7-x86_64 = "*"
mesa-libgl-cos7-x86_64 = "*"
mesa-libgl-devel-cos7-x86_64 = "*"
mold = "*"
pixman-cos7-x86_64 = "*"
sed = "*"
sysroot_linux-64 = "*"
@@ -78,6 +98,8 @@ xorg-xproto = "*"
## Linux Dependencies (aarch64)
[target.linux-aarch64.dependencies]
clang = "*"
clangxx = "*"
kernel-headers_linux-aarch64 = "*"
libdrm-cos7-aarch64 = "*"
libglvnd-cos7-aarch64 = "*"
@@ -95,6 +117,7 @@ libxfixes-cos7-aarch64 = "*"
libxi-cos7-aarch64 = "*"
libxi-devel-cos7-aarch64 = "*"
libxxf86vm-cos7-aarch64 = "*"
llvm-openmp = "*"
mesa-dri-drivers-cos7-aarch64 = "*"
mesa-khr-devel-cos7-aarch64 = "*"
mesa-libegl-cos7-aarch64 = "*"
@@ -103,6 +126,7 @@ mesa-libgbm-cos7-aarch64 = "*"
mesa-libgl-cos7-aarch64 = "*"
mesa-libgl-devel-cos7-aarch64 = "*"
mesa-libglapi-cos7-aarch64 = "*"
mold = "*"
pixman-cos7-aarch64 = "*"
sed = "*"
sysroot_linux-aarch64 = "*"
@@ -118,31 +142,34 @@ sed = "*"
[target.osx-arm64.dependencies]
sed = "*"
[target.win-64.dependencies]
pthreads-win32 = "*"
## Qt 6 Configuration Presets
[target.linux-64.tasks]
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.linux-aarch64.tasks]
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"]}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"]}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"]}
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-linux-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.osx-64.tasks]
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.osx-arm64.tasks]
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-macos-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.win-64.tasks]
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-windows-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-windows-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-windows-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"]}
configure = { cmd = [ "cmake", "-B", "build", "--preset", "conda-windows-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "-B", "build", "--preset", "conda-windows-debug", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "-B", "build", "--preset", "conda-windows-release", "-DFREECAD_QT_VERSION=6", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
freecad = { cmd = [ ".pixi/envs/default/Library/bin/FreeCAD.exe" ], depends-on = ["install"]}
## Tasks

View File

@@ -1,16 +1,16 @@
area==1.1.1
cog==0.6.1
ConfigParser==5.3.0
ConfigParser==7.2.0
defusedxml==0.7.1
ifcopenshell==0.7.0.230318
ladybug-core==0.42.2
matplotlib==3.6.3
numpy==1.24.2
opencamlib==2023.1.11
packaging==23.0
packaging==24.2
Pivy==0.6.8
ply==3.11
debugpy==1.6.7
debugpy==1.8.14
pyNastran==1.3.4
pyshp==2.3.1
PySide2==5.15.2.1

View File

@@ -4,7 +4,14 @@ if (BUILD_SMESH)
endif()
add_subdirectory(lazy_loader)
add_subdirectory(libE57Format)
if(NOT FREECAD_USE_EXTERNAL_E57FORMAT)
add_subdirectory(libE57Format)
endif()
if(BUILD_MATERIAL_EXTERNAL)
add_subdirectory(lru-cache)
endif()
if (BUILD_ASSEMBLY AND NOT FREECAD_USE_EXTERNAL_ONDSELSOLVER)
if( NOT EXISTS "${CMAKE_SOURCE_DIR}/src/3rdParty/OndselSolver/CMakeLists.txt" )

View File

@@ -1,5 +1,5 @@
cmake_minimum_required (VERSION 2.8.12)
project (libkdtree CXX)
cmake_minimum_required (VERSION 2.6.0)
option (BUILD_PYTHON_BINDINGS "Build Python bindings (requires SWIG)")

View File

@@ -1,6 +1,6 @@
"The Artistic Licence 2.0"
Copyright (c) 2000-2006, The Perl Foundation.
https://www.perlfoundation.org/artistic-license-20.html
http://www.perlfoundation.org/legal/licenses/artistic-2_0.html
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.

View File

@@ -1,236 +0,0 @@
libkdtree++ ChangeLog
=====================
2008-11-17 Sylvain Bougerel <sylvain.bougerel@asia.thalesgroup.com>
- Added #include<cstdio> in order to compile 'printf' statements in the
file 'examples/test_find_within_range.cpp'.
- Added patch from Max Fellermann in order to compile libkdtree++ with
clang++.
2009-02-10 Paul Harris <paulharris@computer.org>
- Bug fix: was incorrectly casting a pointer when the search key type
was different to the stored type.
2008-12-30 Paul Harris <paulharris@computer.org>
- New function: efficient_replace_and_optimise().
Yes, its a long name. Sylvain doesn't like it.
The reason for the long name is that it is a dangerous function,
and it will resort whatever vector<> of data that you pass it.
So I wanted the user to really know what they were doing before
they called this function.
- Now calls sqrt() when required in order to search for items
in 'real' distance units... And so it will accept and return distance
in 'real' units (as opposed to squared units).
This is not an ideal solution, we have all sorts of ideas to improve
kdtree which will include less calls to sqrt() for more speed, and
the ability to change the standard euclidean distance measurements
for distance-on-a-sphere or whatever the user wants.
- Changed from using std::sort() to std::nth_element() when optimising
the tree. Performance boost.
- Added lots of tests to check that the find functions are working
correctly when fed edge-cases, including:
- Items that are exactly 'max' distance away from the target.
- When there are no value items to find.
- Templated the find functions so that the target/center point can be
anything that can be accessed via the Accessor.
- Fixes to make it compile.
- And, a Python wrapper ! See README.Python
- CMake support now can build the python wrapper and install the headers
and the python wrapper to a destination folder. Its simple, but neat.
Does not install python module into the python site packages or anything
like that.
2008-11-17 Sylvain Bougerel <sylvain.bougerel@asia.thalesgroup.com>
- The version number of the library is now part of the headers.
- Fixed a bug with assignment operator.
- Fixed uninitialized memory problem with valgrind, when printing the
content of the tree. Due to the fact the _M_header was a _Link_type
instead of a _Node_base type and _M_root was a _Base_ptr type instead of
a _Link_type.
- Paul Harris fixed find() by ensuring that the correct node is being
matched during a find(). Thus, fixed a similar problem in erase. Paul
also added a new test courtesy of Hayne.
- Paul Harris augmented test_kdtree with various test on copy
construction, assignment, and formatting operator.
- Paul Harris added support for CMake, which should suit not only
MSVC users but others too.
- Paul Harris fixed bug with compiling with MSVC2005 with the 64bit
warnings turned on.
2008-11-12 Sylvain Bougerel <sylvain.bougerel@asia.thalesgroup.com>
- Fix segfault on the regular iterator when _M_header->_M_right ==
_M_root. Fix segfault on the reverse iterator when _M_header->_M_left ==
_M_root.
Besides, it also change the behavior when iterating past the end() or
rend(). Previously this would result in segfaults, now it makes the
iterator points to an undetermined location in the tree, similarly to
the current implementation of GNU libstdc++.
2008-11-10 Sylvain Bougerel <sylvain.bougerel@asia.thalesgroup.com>
- kdtree++/iterator.hpp (KDTree): the decrement iterator was
ill-written. Its buggy behavior, and the use of non-standard
reverse_iterator initialiser needed to be fixed. These error were do to
a previous failed attempt by me at fixing the reverse_iterator.
This time, I believe I got it right, however it needed the kdtree
structure to be modified. The reason is that without modification it is
not possible to distinguish the "header" from the "root" within the
iterator. This is required for the reverse_iterator to work properly.
Now the kdtree has an additional pointer that points directly to the
root. The parent pointer of the header is permanently null. And
therefore the header can be distinguished from the root within the
iterator by checking the parent of the current node: if it is null, we
are at the header.
2008-11-10 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com)
- patch from Martin Shreiber to make libkdtree to compile with newer
version of g++-4.2 and g++4.3.
- patch from Paul Harris to make libkdtree more exception transparent.
2007-12-08 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com)
- fix bug where find_nearest() could return the wrong value if a
maximum distance greater than the root distance to the target value
was given in argument to the function.
- find_nearest() still returns SQUARED value of the distance. You still
have to use sqrt() on the second member of the iterator.
- find_nearest() behavior was slightly changed: if many nodes are at
the same distance from the target value, the node with the lowest
memory address will be returned. This is to catter for the
reimplementation of find_exact() coming soon.
2007-12-02 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com)
- find_nearest() now returned the SQUARED value of the distance for
euclidean space calculation (the default). You have to use sqrt() on
the returned distance (i.e. iterator->second) if you want to read the
absolute distance returned by find_nearest. My apologies for not
making highlighting this beforehand.
- Increased the performance of find and find_nearest/find_nearest_if by
about 50x to 100x depending on your compilation flags.
- KDTree are not defined as:
KDTree<__K, _Val, _Acc, _Cmp, _Alloc>
but as:
KDTree<__K, _Val, _Acc, _Dist, _Cmp, _Alloc>
So pay attention to the _Dist functor. The distance functor calculate
the squared difference between 2 elements returned by the accessor. You
might have to change your code to reflect the new definition, or it wont
compile if you have set custom accessor and comparison functors.
- The following functors are now accessible in the tree:
- the comparison functor, accessible by copy only
- the accessor functor, accessible by copy only
- the distance functor, accessible read-write, this means that
you can modify the behavior of find, find_nearest,
find_nearest_if within the same KDTree object.
- find_exact has not be modified and retained the code of the former,
slower algorithm. I have to write some more code to do this. Pls wait a
little more.
- The file accessor.hpp was renamed as function.hpp for it now boast
more than just the KDTree accessor
2007-11-25 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com)
- fixed the reverse_iterator. Now it can be used.
2007-10-24 Sylvain Bougerel (sylvain.bougerel.devel@gmail.com)
- Removal of all the warnings that are yield by the compiler when
using the following flags:
-Wall -pedantic -ansi
Do not hesitate to suggest any flags for additional code checking.
This release also feature numerous of enhancements by Paul Harris
(paulharris@computer.org):
- const kdtrees can be searched
- find_nearest_if() enforce validation of a predicate
- visit_within_range() walk the tree and calls
Visitor::operator() on template argument <Visitor> for
each node within the range
- find_exact() matches an kdtree::value_type by location and by
calling kdtree::value_type::operator==() (in case two different
items have the same location find_exact() will not return the
wrong item)
- erase_exact() is to erase() what find_exact() is to find()
- check_tree() and num_dist_calcs for debugging purpose plus
additional improvements on erase and region intersection
2004-11-26 Paul Harris (paulharris@computer.org)
- New feature: find_nearest()
- Accessors can now be initialised with the tree, so ptr_fun()
or functors can be used to access datapoints.
- Accessors now much more generic, so you can use the same
accessor to access multiple types.
- Range-constructors now optimise() automatically, simplifying
the construction of a filled tree.
- _Range is now more easy to construct.
2004-11-15 Martin F. Krafft (libkdtree@pobox.madduck.net)
- fixed numerous little bugs that led to compilation problems
- changed code to compile cleanly with GCC 3.4 and GCC 4.0
2004-11-06 Martin F. Krafft (libkdtree@pobox.madduck.net)
- reverted to optimise() to prevent API change, and added an optimize()
passthrough method with an appropriate comment.
2004-11-05 Paul Harris (paulharris@computer.org)
- Renamed optimise() to optimize().
- Added a full set of range constructors and insert(range) methods.
it now works with inserter(tree,tree.begin())
- Target type no longer needs a default constructor. This also fixes
problems with empty trees (would crash if optimized).
- Some code cleanup (removed inlines, switched from const_iterator to
iterator, added this-> to ensure the methods are called).
- Added a new method: count_within_range().
- Fixed bug in rend().
2004-11-04 Martin F. Krafft (libkdtree@pobox.madduck.net)
- Integrated patch by Paul Harris to fix a logic error pertaining to
OutputIterators in find_within_range. find_within_range() now
returns the output iterator instead of a count. Thanks, Paul!
- Added another fix by Paul Harris to _M_get_j_max, which would cause
a dimensional overflow for trees with depths >= K. Thanks (again) Paul!
- Made some improvements to the autotools files.
2004-05-11 Martin F. Krafft (libkdtree@pobox.madduck.net)
- Fixed CFlags and Libs entries in pkgconfig file.
2004-05-11 Martin F. Krafft (libkdtree@pobox.madduck.net)
- Initial release.
COPYRIGHT --
libkdtree++ is (c) 2004-2007 Martin F. Krafft <libkdtree@pobox.madduck.net> and
Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> and distributed under the
terms of the Artistic License 2.0. See the ./COPYING file in the source tree
root for more information.
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
FITNESS FOR A PARTICULAR PURPOSE.

View File

@@ -24,12 +24,12 @@ Please leave bugreports on Github Issues page <https://github.com/nvmd/libkdtree
Historical background
---------------------
In the past, this library was available from [http://libkdtree.alioth.debian.org/](https://web.archive.org/web/20180422154001/http://libkdtree.alioth.debian.org/).
This page seems to be gone now, available only via [WebArchive](https://web.archive.org/web/20180422154001/http://libkdtree.alioth.debian.org/).
In the past, this library was available from <http://libkdtree.alioth.debian.org/>.
This page seems to be gone now, available only via WebArchive.
This is a mirror and a fork of that original repository, created in
2011 and maintained ever since.
Notes of the original author are preserved below.
Notes of the original author a preserved below.
Installation
------------
@@ -114,8 +114,8 @@ without the help of a number of people. Foremost, I would like to thank the
folks from the #c++ channel on Freenode, specifically (in no particular order)
orbitz, quix, Erwin, pwned, wcstok, dasOp, Chaku, Adrinael, The_Vulture, and
LIM2 (if I left anyone out, let me know). Finally, I thank the Artificial
Intelligence Laboratory of the University of Zurich, Dr. Peter Eggenberger, and
Gabriel Gómez for giving me the opportunity to write this stuff.
Intelligence Laboratory of the University of Zurich, Dr. Peter Eggenberger and
Gabriel Gómez for giving me the opportunity to write this stuff.
Since libkdtree++ makes an effort to stay as close as possible to the feel of
a STL container, concepts and inspiration was gained from the SGI C++

View File

@@ -1,3 +0,0 @@
add_executable (test_hayne test_hayne.cpp)
add_executable (test_kdtree test_kdtree.cpp)
add_executable (test_find_within_range test_find_within_range.cpp)

View File

@@ -1,108 +0,0 @@
// Thanks to James Remillard
//
#include <cstdio>
#include <kdtree++/kdtree.hpp>
#include <vector>
#include <map>
#include <set>
using namespace std;
struct kdtreeNode
{
typedef double value_type;
double xyz[3];
size_t index;
value_type operator[](size_t n) const
{
return xyz[n];
}
double distance( const kdtreeNode &node)
{
double x = xyz[0] - node.xyz[0];
double y = xyz[1] - node.xyz[1];
double z = xyz[2] - node.xyz[2];
// this is not correct return sqrt( x*x+y*y+z*z);
// this is what kdtree checks with find_within_range()
// the "manhattan distance" from the search point.
// effectively, distance is the maximum distance in any one dimension.
return max(fabs(x),max(fabs(y),fabs(z)));
}
};
int main(int argc,char *argv[])
{
vector<kdtreeNode> pts;
typedef KDTree::KDTree<3,kdtreeNode> treeType;
treeType tree;
// make random 3d points
for ( size_t n = 0; n < 10000; ++n)
{
kdtreeNode node;
node.xyz[0] = double(rand())/RAND_MAX;
node.xyz[1] = double(rand())/RAND_MAX;
node.xyz[2] = double(rand())/RAND_MAX;
node.index = n;
tree.insert( node);
pts.push_back( node);
}
for (size_t r = 0; r < 1000; ++r)
{
kdtreeNode refNode;
refNode.xyz[0] = double(rand())/RAND_MAX;
refNode.xyz[1] = double(rand())/RAND_MAX;
refNode.xyz[2] = double(rand())/RAND_MAX;
double limit = double(rand())/RAND_MAX;
// find the correct return list by checking every single point
set<size_t> correctCloseList;
for ( size_t i= 0; i < pts.size(); ++i)
{
double dist = refNode.distance( pts[i]);
if ( dist < limit)
correctCloseList.insert( i );
}
// now do the same with the kdtree.
vector<kdtreeNode> howClose;
tree.find_within_range(refNode,limit,back_insert_iterator<vector<kdtreeNode> >(howClose));
// make sure no extra points are returned, and the return has no missing points.
for ( size_t i = 0; i < howClose.size(); ++i)
{
set<size_t>::iterator hit = correctCloseList.find( howClose[i].index);
if ( hit != correctCloseList.end())
{
correctCloseList.erase(hit);
}
else
{
// point that is too far away - fail!
assert(false);
printf("fail, extra points.\n");
}
}
// fail, not all of the close enough points returned.
assert( correctCloseList.size() == 0);
if ( correctCloseList.size() > 0)
{
printf("fail, missing points.\n");
}
}
}

View File

@@ -1,116 +0,0 @@
#define KDTREE_SIZE_T unsigned int
#include <kdtree++/kdtree.hpp>
#include <vector>
#include <limits>
#include <iostream>
#include <functional>
using namespace std;
struct duplet
{
typedef int value_type;
inline value_type operator[](int const N) const { return d[N]; }
inline bool operator==(duplet const& other) const
{
return this->d[0] == other.d[0] && this->d[1] == other.d[1];
}
inline bool operator!=(duplet const& other) const
{
return this->d[0] != other.d[0] || this->d[1] != other.d[1];
}
friend ostream & operator<<(ostream & o, duplet const& d)
{
return o << "(" << d[0] << "," << d[1] << ")";
}
value_type d[2];
};
typedef KDTree::KDTree<2, duplet, std::pointer_to_binary_function<duplet,int,double> > duplet_tree_type;
inline double return_dup( duplet d, int k ) { return d[k]; }
int main()
{
duplet_tree_type dupl_tree_test(std::ptr_fun(return_dup));
std::vector<duplet> vDuplets;
//srand(time(0));
int randy1 = 0;
int randy2 = 0;
for (int i=0; i<700; i++)
{
//create coordinate for new duplet
randy1+=2;
randy1=randy1%255;
randy2+=3;
randy2=randy2%255;
//randy1 = rand() % 255;
//randy2 = rand() % 255;
//new duplet
duplet super_dupre = { {randy1, randy2} };
//check if duplet with same coordinate already in vector/tree. If not: insert in vector and tree
duplet_tree_type::iterator pItr = dupl_tree_test.find_nearest(super_dupre,std::numeric_limits<double>::max()).first;
if (*pItr!=super_dupre)
{
dupl_tree_test.insert(super_dupre);
vDuplets.push_back(super_dupre);
}
}
dupl_tree_test.optimise();
size_t elements;
while (vDuplets.size() > 0) //delete all duplets from tree which are in the vector
{
elements = vDuplets.size();
duplet element_to_erase = vDuplets.back();
vDuplets.pop_back();
if (vDuplets.size() == 147)
cout << "THIS IS THE BUG TRIGGER" << endl;
cout << vDuplets.size() << " : Deleting " << element_to_erase << endl;
assert( find(dupl_tree_test.begin(),dupl_tree_test.end(), element_to_erase) != dupl_tree_test.end() );
assert(dupl_tree_test.find(element_to_erase) != dupl_tree_test.end());
duplet_tree_type::iterator will = dupl_tree_test.find(element_to_erase);
duplet_tree_type::iterator should = dupl_tree_test.find_exact(element_to_erase);
cout << " tree will delete: " << *will << endl;
cout << " tree should delete: " << *should << endl;
assert(*will == *should);
dupl_tree_test.erase(element_to_erase); //erase() : will probably erase wrong element sooner or later
//dupl_tree_test.erase_exact(element_to_erase); --> this works
// now check that it cannot find the element UNLESS there is another one with the identical location in the list...
if (find(vDuplets.begin(),vDuplets.end(),element_to_erase) == vDuplets.end())
{
duplet_tree_type::iterator not_there = dupl_tree_test.find(element_to_erase);
if (not_there != dupl_tree_test.end())
{
cout << "SHOULD NOT HAVE FOUND THIS: " << *not_there << endl;
assert(0);
}
else
{
cout << " find() double-check passed." << endl;
}
}
}
}

View File

@@ -1,424 +0,0 @@
#define KDTREE_DEFINE_OSTREAM_OPERATORS
// Make SURE all our asserts() are checked
#undef NDEBUG
#include <kdtree++/kdtree.hpp>
#include <deque>
#include <iostream>
#include <vector>
#include <limits>
#include <functional>
#include <set>
// used to ensure all triplets that are accessed via the operator<< are initialised.
std::set<const void*> registered;
struct triplet
{
typedef double value_type;
triplet(value_type a, value_type b, value_type c)
{
d[0] = a;
d[1] = b;
d[2] = c;
bool reg_ok = (registered.find(this) == registered.end());
assert(reg_ok);
bool reg_inserted_ok = registered.insert(this).second;
assert(reg_inserted_ok);
}
triplet(const triplet & x)
{
d[0] = x.d[0];
d[1] = x.d[1];
d[2] = x.d[2];
bool reg_ok = (registered.find(this) == registered.end());
assert(reg_ok);
bool reg_inserted_ok = registered.insert(this).second;
assert(reg_inserted_ok);
}
~triplet()
{
bool unreg_ok = (registered.find(this) != registered.end());
assert(unreg_ok);
registered.erase(this);
}
double distance_to(triplet const& x) const
{
double dist = 0;
for (int i = 0; i != 3; ++i)
dist += (d[i]-x.d[i])*(d[i]-x.d[i]);
return std::sqrt(dist);
}
inline value_type operator[](size_t const N) const { return d[N]; }
value_type d[3];
};
// same as triplet, except with the values reversed.
struct alternate_triplet
{
typedef double value_type;
alternate_triplet(const triplet & x)
{
d[0] = x.d[2];
d[1] = x.d[1];
d[2] = x.d[0];
}
inline value_type operator[](size_t const N) const { return d[2-N]; }
value_type d[3];
};
inline bool operator==(triplet const& A, triplet const& B) {
return A.d[0] == B.d[0] && A.d[1] == B.d[1] && A.d[2] == B.d[2];
}
std::ostream& operator<<(std::ostream& out, triplet const& T)
{
assert(registered.find(&T) != registered.end());
return out << '(' << T.d[0] << ',' << T.d[1] << ',' << T.d[2] << ')';
}
inline double tac( triplet t, size_t k ) { return t[k]; }
// use tac as a class instead of a function,
// can access more than one type with just 1 definition.
struct alternate_tac
{
typedef double result_type;
double operator()( triplet const& t, size_t k ) const { return t[k]; }
double operator()( alternate_triplet const& t, size_t k ) const { return t[k]; }
};
typedef KDTree::KDTree<3, triplet, std::pointer_to_binary_function<triplet,size_t,double> > tree_type;
struct Predicate
{
bool operator()( triplet const& t ) const
{
return t[0] > 3; // anything, we are currently testing that it compiles.
}
};
// never finds anything
struct FalsePredicate
{
bool operator()( triplet const& t ) const { return false; }
};
int main()
{
// check that it'll find nodes exactly MAX away
{
tree_type exact_dist(std::ptr_fun(tac));
triplet c0(5, 4, 0);
exact_dist.insert(c0);
triplet target(7,4,0);
std::pair<tree_type::const_iterator,double> found = exact_dist.find_nearest(target,2);
assert(found.first != exact_dist.end());
assert(found.second == 2);
std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << std::endl;
}
// do the same test, except use alternate_triplet as the search key
{
// NOTE: stores triplet, but we search with alternate_triplet
typedef KDTree::KDTree<3, triplet, alternate_tac> alt_tree;
triplet actual_target(7,0,0);
alt_tree tree;
tree.insert( triplet(0, 0, 7) );
tree.insert( triplet(0, 0, 7) );
tree.insert( triplet(0, 0, 7) );
tree.insert( triplet(3, 0, 0) );
tree.insert( actual_target );
tree.optimise();
alternate_triplet target( actual_target );
std::pair<alt_tree::const_iterator,double> found = tree.find_nearest(target);
assert(found.first != tree.end());
std::cout << "Test with alternate search type, found: " << *found.first << ", wanted " << actual_target << std::endl;
assert(found.second == 0);
assert(*found.first == actual_target);
}
{
tree_type exact_dist(std::ptr_fun(tac));
triplet c0(5, 2, 0);
exact_dist.insert(c0);
triplet target(7,4,0);
// call find_nearest without a range value - it found a compile error earlier.
std::pair<tree_type::const_iterator,double> found = exact_dist.find_nearest(target);
assert(found.first != exact_dist.end());
std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << " @ " << found.second << " should be " << std::sqrt(8) << std::endl;
assert(found.second == std::sqrt(8));
}
{
tree_type exact_dist(std::ptr_fun(tac));
triplet c0(5, 2, 0);
exact_dist.insert(c0);
triplet target(7,4,0);
std::pair<tree_type::const_iterator,double> found = exact_dist.find_nearest(target,std::sqrt(8));
assert(found.first != exact_dist.end());
std::cout << "Test find_nearest(), found at exact distance away from " << target << ", found " << *found.first << " @ " << found.second << " should be " << std::sqrt(8) << std::endl;
assert(found.second == std::sqrt(8));
}
tree_type src(std::ptr_fun(tac));
triplet c0(5, 4, 0); src.insert(c0);
triplet c1(4, 2, 1); src.insert(c1);
triplet c2(7, 6, 9); src.insert(c2);
triplet c3(2, 2, 1); src.insert(c3);
triplet c4(8, 0, 5); src.insert(c4);
triplet c5(5, 7, 0); src.insert(c5);
triplet c6(3, 3, 8); src.insert(c6);
triplet c7(9, 7, 3); src.insert(c7);
triplet c8(2, 2, 6); src.insert(c8);
triplet c9(2, 0, 6); src.insert(c9);
std::cout << src << std::endl;
src.erase(c0);
src.erase(c1);
src.erase(c3);
src.erase(c5);
src.optimise();
// test the efficient_replace_and_optimise()
tree_type eff_repl = src;
{
std::vector<triplet> vec;
// erased above as part of test vec.push_back(triplet(5, 4, 0));
// erased above as part of test vec.push_back(triplet(4, 2, 1));
vec.push_back(triplet(7, 6, 9));
// erased above as part of test vec.push_back(triplet(2, 2, 1));
vec.push_back(triplet(8, 0, 5));
// erased above as part of test vec.push_back(triplet(5, 7, 0));
vec.push_back(triplet(3, 3, 8));
vec.push_back(triplet(9, 7, 3));
vec.push_back(triplet(2, 2, 6));
vec.push_back(triplet(2, 0, 6));
eff_repl.clear();
eff_repl.efficient_replace_and_optimise(vec);
}
std::cout << std::endl << src << std::endl;
tree_type copied(src);
std::cout << copied << std::endl;
tree_type assigned = src;
std::cout << assigned << std::endl;
for (int loop = 0; loop != 4; ++loop)
{
tree_type * target;
switch (loop)
{
case 0: std::cout << "Testing plain construction" << std::endl;
target = &src;
break;
case 1: std::cout << "Testing copy-construction" << std::endl;
target = &copied;
break;
case 2: std::cout << "Testing assign-construction" << std::endl;
target = &assigned;
break;
default:
case 4: std::cout << "Testing efficient-replace-and-optimise" << std::endl;
target = &eff_repl;
break;
}
tree_type & t = *target;
int i=0;
for (tree_type::const_iterator iter=t.begin(); iter!=t.end(); ++iter, ++i);
std::cout << "iterator walked through " << i << " nodes in total" << std::endl;
if (i!=6)
{
std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl;
return 1;
}
i=0;
for (tree_type::const_reverse_iterator iter=t.rbegin(); iter!=t.rend(); ++iter, ++i);
std::cout << "reverse_iterator walked through " << i << " nodes in total" << std::endl;
if (i!=6)
{
std::cerr << "Error: does not tally with the expected number of nodes (6)" << std::endl;
return 1;
}
triplet s(5, 4, 3);
std::vector<triplet> v;
unsigned int const RANGE = 3;
size_t count = t.count_within_range(s, RANGE);
std::cout << "counted " << count
<< " nodes within range " << RANGE << " of " << s << ".\n";
t.find_within_range(s, RANGE, std::back_inserter(v));
std::cout << "found " << v.size() << " nodes within range " << RANGE
<< " of " << s << ":\n";
std::vector<triplet>::const_iterator ci = v.begin();
for (; ci != v.end(); ++ci)
std::cout << *ci << " ";
std::cout << "\n" << std::endl;
std::cout << std::endl << t << std::endl;
// search for all the nodes at exactly 0 dist away
for (tree_type::const_iterator target = t.begin(); target != t.end(); ++target)
{
std::pair<tree_type::const_iterator,double> found = t.find_nearest(*target,0);
assert(found.first != t.end());
assert(*found.first == *target);
std::cout << "Test find_nearest(), found at exact distance away from " << *target << ", found " << *found.first << std::endl;
}
{
const double small_dist = 0.0001;
std::pair<tree_type::const_iterator,double> notfound = t.find_nearest(s,small_dist);
std::cout << "Test find_nearest(), nearest to " << s << " within " << small_dist << " should not be found" << std::endl;
if (notfound.first != t.end())
{
std::cout << "ERROR found a node at dist " << notfound.second << " : " << *notfound.first << std::endl;
std::cout << "Actual distance = " << s.distance_to(*notfound.first) << std::endl;
}
assert(notfound.first == t.end());
}
{
std::pair<tree_type::const_iterator,double> nif = t.find_nearest_if(s,std::numeric_limits<double>::max(),Predicate());
std::cout << "Test find_nearest_if(), nearest to " << s << " @ " << nif.second << ": " << *nif.first << std::endl;
std::pair<tree_type::const_iterator,double> cantfind = t.find_nearest_if(s,std::numeric_limits<double>::max(),FalsePredicate());
std::cout << "Test find_nearest_if(), nearest to " << s << " should never be found (predicate too strong)" << std::endl;
assert(cantfind.first == t.end());
}
{
std::pair<tree_type::const_iterator,double> found = t.find_nearest(s,std::numeric_limits<double>::max());
std::cout << "Nearest to " << s << " @ " << found.second << " " << *found.first << std::endl;
std::cout << "Should be " << found.first->distance_to(s) << std::endl;
// NOTE: the assert does not check for an exact match, as it is not exact when -O2 or -O3 is
// switched on. Some sort of optimisation makes the math inexact.
assert( fabs(found.second - found.first->distance_to(s)) < std::numeric_limits<double>::epsilon() );
}
{
triplet s2(10, 10, 2);
std::pair<tree_type::const_iterator,double> found = t.find_nearest(s2,std::numeric_limits<double>::max());
std::cout << "Nearest to " << s2 << " @ " << found.second << " " << *found.first << std::endl;
std::cout << "Should be " << found.first->distance_to(s2) << std::endl;
// NOTE: the assert does not check for an exact match, as it is not exact when -O2 or -O3 is
// switched on. Some sort of optimisation makes the math inexact.
assert( fabs(found.second - found.first->distance_to(s2)) < std::numeric_limits<double>::epsilon() );
}
std::cout << std::endl;
std::cout << t << std::endl;
// Testing iterators
{
std::cout << "Testing iterators" << std::endl;
t.erase(c2);
t.erase(c4);
t.erase(c6);
t.erase(c7);
t.erase(c8);
// t.erase(c9);
std::cout << std::endl << t << std::endl;
std::cout << "Forward iterator test..." << std::endl;
std::vector<triplet> forwards;
for (tree_type::iterator i = t.begin(); i != t.end(); ++i)
{ std::cout << *i << " " << std::flush; forwards.push_back(*i); }
std::cout << std::endl;
std::cout << "Reverse iterator test..." << std::endl;
std::vector<triplet> backwards;
for (tree_type::reverse_iterator i = t.rbegin(); i != t.rend(); ++i)
{ std::cout << *i << " " << std::flush; backwards.push_back(*i); }
std::cout << std::endl;
std::reverse(backwards.begin(),backwards.end());
assert(backwards == forwards);
}
}
// Walter reported that the find_within_range() wasn't giving results that were within
// the specified range... this is the test.
{
tree_type tree(std::ptr_fun(tac));
tree.insert( triplet(28.771200,16.921600,-2.665970) );
tree.insert( triplet(28.553101,18.649700,-2.155560) );
tree.insert( triplet(28.107500,20.341400,-1.188940) );
tree.optimise();
std::deque< triplet > vectors;
triplet sv(18.892500,20.341400,-1.188940);
tree.find_within_range(sv, 10.0f, std::back_inserter(vectors));
std::cout << std::endl << "Test find_with_range( " << sv << ", 10.0f) found " << vectors.size() << " candidates." << std::endl;
// double-check the ranges
for (std::deque<triplet>::iterator v = vectors.begin(); v != vectors.end(); ++v)
{
double dist = sv.distance_to(*v);
std::cout << " " << *v << " dist=" << dist << std::endl;
if (dist > 10.0f)
std::cout << " This point is too far! But that is by design, its within a 'box' with a 'radius' of 10, not a sphere with a radius of 10" << std::endl;
// Not a valid test, it can be greater than 10 if the point is in the corners of the box.
// assert(dist <= 10.0f);
}
}
return 0;
}
/* COPYRIGHT --
*
* This file is part of libkdtree++, a C++ template KD-Tree sorting container.
* libkdtree++ is (c) 2004-2007 Martin F. Krafft <libkdtree@pobox.madduck.net>
* and Sylvain Bougerel <sylvain.bougerel.devel@gmail.com> distributed under the
* terms of the Artistic License 2.0. See the ./COPYING file in the source tree
* root for more information.
*
* THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
* OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

View File

@@ -54,8 +54,8 @@ namespace KDTree
inline _Base_iterator(_Base_const_ptr const __N = NULL)
: _M_node(__N) {}
//inline _Base_iterator(_Base_iterator const& __THAT)
// : _M_node(__THAT._M_node) {}
inline _Base_iterator(_Base_iterator const& __THAT)
: _M_node(__THAT._M_node) {}
inline void
_M_increment()

View File

@@ -53,12 +53,12 @@
// KDTREE_VERSION % 100 is the patch level
// KDTREE_VERSION / 100 % 1000 is the minor version
// KDTREE_VERSION / 100000 is the major version
#define KDTREE_VERSION 702
#define KDTREE_VERSION 704
//
// KDTREE_LIB_VERSION must be defined to be the same as KDTREE_VERSION
// but as a *string* in the form "x_y[_z]" where x is the major version
// number, y is the minor version number, and z is the patch level if not 0.
#define KDTREE_LIB_VERSION "0_7_2"
#define KDTREE_LIB_VERSION "0_7_4"
#include <vector>
@@ -1214,7 +1214,6 @@ protected:
o << "dimensions: " << __K << std::endl;
typedef KDTree<__K, _Val, _Acc, _Dist, _Cmp, _Alloc> _Tree;
typedef typename _Tree::_Link_type _Link_type;
std::stack<_Link_const_type> s;
s.push(tree._M_get_root());

View File

@@ -27,7 +27,7 @@ namespace KDTree
typedef std::pair<_Region,_SubVal> _CenterPt;
_Region(_Acc const& __acc=_Acc(), const _Cmp& __cmp=_Cmp())
: _M_cmp(__cmp), _M_acc(__acc) {}
: _M_acc(__acc), _M_cmp(__cmp) {}
template <typename Val>
_Region(Val const& __V,

View File

@@ -10,7 +10,7 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/..)
# Build the _kdtree python module
set_source_files_properties (py-kdtree.i PROPERTIES CPLUSPLUS ON)
swig_add_module (kdtree python py-kdtree.i)
swig_link_libraries (kdtree ${Python3_LIBRARIES})
swig_link_libraries (kdtree ${PYTHON_LIBRARIES})
# Copy the test file into the build dir
install (FILES py-kdtree_test.py DESTINATION ${CMAKE_INSTALL_PREFIX}/python)

73
src/3rdParty/lru-cache/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,73 @@
###########################################################
## CMAKE SETUP
###########################################################
cmake_minimum_required(VERSION 3.2)
project(lru-cache)
add_compile_options(-g)
########################################
# C++ VERSIONING
########################################
include(CheckCXXCompilerFlag)
# check_cxx_compiler_flag("-std=c++14" COMPILER_SUPPORTS_CXX_14)
# check_cxx_compiler_flag("-std=c++1z" COMPILER_SUPPORTS_CXX_1z)
# check_cxx_compiler_flag("-std=c++17" COMPILER_SUPPORTS_CXX_17)
# if (COMPILER_SUPPORTS_CXX_1z)
# message(STATUS "Compiling with C++1z")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z")
# elseif (COMPILER_SUPPORTS_CXX_14)
# message(STATUS "Compiling with C++14")
# set(CMAKE_CXX_STANDARD 14)
# elseif (COMPILER_SUPPORTS_CXX_17)
# message(STATUS "Compiling with C++17")
# set(CMAKE_CXX_STANDARD 17)
# else()
# message(FATAL_ERROR "Please install a modern C++ compiler, they are not expensive.")
# endif()
###########################################################
## DEPENDENCIES
###########################################################
set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/Modules/"
)
###########################################################
## INCLUDES
###########################################################
# Need this top-level include for "tests/"
# include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
###########################################################
## EXAMPLES
###########################################################
# add_subdirectory(examples)
########################################
# TESTS
########################################
# option(BUILD_LRU_CACHE_TESTS "Enable tests" OFF)
# if(BUILD_LRU_CACHE_TESTS)
# message(STATUS "Enabling tests ...")
# enable_testing()
# add_subdirectory(tests)
# else()
# message(STATUS "Disabling tests ...")
# endif()
file (GLOB LRU_HEADERS include/*.hpp)
file (GLOB LRU_INTERNAL_HEADERS include/*.hpp)
install (FILES ${LRU_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
install (FILES ${LRU_INTERNAL_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/internal)

19
src/3rdParty/lru-cache/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
The MIT License (MIT)
Copyright (c) 2016 Peter Goldsborough
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

304
src/3rdParty/lru-cache/README.md vendored Normal file

File diff suppressed because one or more lines are too long

3
src/3rdParty/lru-cache/cpplint.cfg vendored Normal file
View File

@@ -0,0 +1,3 @@
# cpplint configuration
filter=-build/c++11,-whitespace/parens,-runtime/references,-whitespace/operators

2310
src/3rdParty/lru-cache/docs/Doxyfile vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
###########################################################
## BINARIES
###########################################################
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples)
########################################
# TARGETS
########################################
add_executable(fibonacci-basic fibonacci-basic.cpp)
add_executable(fibonacci-timed fibonacci-timed.cpp)
add_executable(statistics statistics.cpp)
add_executable(callbacks callbacks.cpp)
add_executable(lowercase lowercase.cpp)
add_executable(wrap wrap.cpp)

View File

@@ -0,0 +1,76 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#include <iostream>
#include "lru/lru.hpp"
using Cache = LRU::Cache<std::uint64_t, std::uint64_t>;
std::uint64_t fibonacci(std::uint64_t n, Cache& cache) {
if (n < 2) return 1;
// We std::uint64_ternally keep track of the last accessed key, meaning a
// `contains(key)` + `lookup(key)` sequence will involve only a single hash
// table lookup.
if (cache.contains(n)) return cache[n];
auto value = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
// Caches are 100% move-aware and we have implemented
// `unordered_map` style emplacement and insertion.
cache.emplace(n, value);
return value;
}
std::uint64_t fibonacci(std::uint64_t n) {
// Use a capacity of 100 (after 100 insertions, the next insertion will evict
// the least-recently inserted element). The default capacity is 128. Note
// that for fibonacci, a capacity of 2 is sufficient (and ideal).
Cache cache(100);
// clang-format off
cache.hit_callback([](auto& key, auto& value) {
std::clog << "Hit for entry ("
<< key << ", " << value << ")"
<< std::endl;
});
cache.miss_callback([](auto& key) {
std::clog << "Miss for " << key<< std::endl;
});
cache.access_callback([](auto& key, bool was_hit) {
std::clog << "Access for " << key
<< " was a " << (was_hit ? "hit" : "miss")
<< std::endl;
});
// clang-format on
auto value = fibonacci(n, cache);
return value;
}
auto main() -> int {
std::cout << fibonacci(10) << std::endl;
}

View File

@@ -0,0 +1,55 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#include <iostream>
#include "lru/lru.hpp"
using Cache = LRU::Cache<int, int>;
int fibonacci(int n, Cache& cache) {
if (n < 2) return 1;
// We internally keep track of the last accessed key, meaning a
// `contains(key)` + `lookup(key)` sequence will involve only a single hash
// table lookup.
if (cache.contains(n)) return cache[n];
auto value = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
// Caches are 100% move-aware and we have implemented
// `unordered_map` style emplacement and insertion.
cache.emplace(n, value);
return value;
}
int fibonacci(int n) {
// Use a capacity of 100 (after 100 insertions, the next insertion will evict
// the least-recently accessed element). The default capacity is 128. Note
// that for fibonacci, a capacity of 2 is sufficient (and ideal).
Cache cache(100);
return fibonacci(n, cache);
}
auto main() -> int {
std::cout << fibonacci(32) << std::endl;
}

View File

@@ -0,0 +1,57 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#include <chrono>
#include <iostream>
#include "lru/lru.hpp"
using namespace std::chrono_literals;
using Cache = LRU::TimedCache<int, int>;
int fibonacci(int n, Cache& cache) {
if (n < 2) return 1;
// We internally keep track of the last accessed key, meaning a
// `contains(key)` + `lookup(key)` sequence will involve only a single hash
// table lookup.
if (cache.contains(n)) return cache[n];
auto value = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
// Caches are 100% move-aware and we have implemented
// `unordered_map` style emplacement and insertion.
cache.emplace(n, value);
return value;
}
int fibonacci(int n) {
// Use a time to live of 100ms. This means that 100ms after insertion, a key
// will be said to have "expired" and `contains(key)` will return false.
Cache cache(100ms);
return fibonacci(n, cache);
}
auto main() -> int {
std::cout << fibonacci(32) << std::endl;
}

View File

@@ -0,0 +1,47 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#include <chrono>
#include <iostream>
#include "lru/lowercase.hpp"
void print(lru::tag::basic_cache) {
std::cout << "basic cache" << '\n';
}
void print(lru::tag::timed_cache) {
std::cout << "timed cache" << '\n';
}
auto main() -> int {
using namespace std::chrono_literals;
lru::cache<int, int> cache;
lru::timed_cache<int, int> timed_cache(100ms);
print(cache.tag());
print(timed_cache.tag());
lru::cache<int, int>::ordered_const_iterator iterator(cache.begin());
lru::statistics<int> stats;
}

View File

@@ -0,0 +1,76 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#include <iostream>
#include "lru/lru.hpp"
using Cache = LRU::Cache<std::uint64_t, std::uint64_t>;
std::uint64_t fibonacci(std::uint64_t n, Cache& cache) {
if (n < 2) return 1;
// We std::uint64_ternally keep track of the last accessed key, meaning a
// `contains(key)` + `lookup(key)` sequence will involve only a single hash
// table lookup.
if (cache.contains(n)) return cache[n];
auto value = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
// Caches are 100% move-aware and we have implemented
// `unordered_map` style emplacement and insertion.
cache.emplace(n, value);
return value;
}
std::uint64_t fibonacci(std::uint64_t n) {
// Use a capacity of 100 (after 100 insertions, the next insertion will evict
// the least-recently inserted element). The default capacity is 128. Note
// that for fibonacci, a capacity of 2 is sufficient (and ideal).
Cache cache(100);
cache.monitor(2, 3, 4, 5);
auto value = fibonacci(n, cache);
for (auto i : {2, 3, 4, 5}) {
auto stats = cache.stats().stats_for(i);
// clang-format off
std::cout << "Statistics for " << i << ": "
<< stats.hits << " hit(s), "
<< stats.misses << " miss(es)."
<< std::endl;
}
// You'll notice we'll always have n - 1 misses, for each time we access
// one of the numbers in [0, n] for the first time.
std::cout << "Overall: "
<< cache.stats().total_hits() << " hit(s), "
<< cache.stats().total_misses() << " miss(es)."
<< std::endl;
// clang-format on
return value;
}
auto main() -> int {
// The last number that fits into a 64 bit unsigned number
std::cout << fibonacci(92) << std::endl;
}

View File

@@ -0,0 +1,45 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#include <chrono>
#include <iostream>
#include <thread>
#include "lru/lru.hpp"
using namespace std::chrono_literals;
int f(int x) {
std::this_thread::sleep_for(1s);
return x;
}
auto main() -> int {
// Use a time-to-live of 2 minutes and a
// capacity of 128 for an LRU::TimedCache
auto wrapped = LRU::timed_wrap(f, 2min, 128);
std::cout << "Slow the first time ..." << '\n';
wrapped(42);
std::cout << "Fast the second time!" << '\n';
wrapped(42);
}

View File

@@ -0,0 +1,40 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_CACHE_TAGS_HPP
#define LRU_CACHE_TAGS_HPP
namespace LRU {
namespace Tag {
struct BasicCache {};
struct TimedCache {};
} // namespace Tag
namespace Lowercase {
namespace tag {
using basic_cache = ::LRU::Tag::BasicCache;
using timed_cache = ::LRU::Tag::TimedCache;
} // namespace tag
} // namespace Lowercase
} // namespace LRU
#endif // LRU_CACHE_TAGS_HPP

View File

@@ -0,0 +1,207 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_CACHE_HPP
#define LRU_CACHE_HPP
#include <cassert>
#include <chrono>
#include <cstddef>
#include <functional>
#include <iterator>
#include <list>
#include <stdexcept>
#include <unordered_map>
#include <lru/cache-tags.hpp>
#include <lru/error.hpp>
#include <lru/internal/base-cache.hpp>
#include <lru/internal/information.hpp>
#include <lru/internal/last-accessed.hpp>
namespace LRU {
namespace Internal {
template <typename Key,
typename Value,
typename HashFunction,
typename KeyEqual>
using UntimedCacheBase = Internal::BaseCache<Key,
Value,
Internal::Information,
HashFunction,
KeyEqual,
Tag::BasicCache>;
} // namespace Internal
/// A basic LRU cache implementation.
///
/// An LRU cache is a fixed-size cache that remembers the order in which
/// elements were inserted into it. When the size of the cache exceeds its
/// capacity, the "least-recently-used" (LRU) element is erased. In our
/// implementation, usage is defined as insertion, but not lookup. That is,
/// looking up an element does not move it to the "front" of the cache (making
/// the operation faster). Only insertions (and erasures) can change the order
/// of elements. The capacity of the cache can be modified at any time.
///
/// \see LRU::TimedCache
template <typename Key,
typename Value,
typename HashFunction = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>>
class Cache
: public Internal::UntimedCacheBase<Key, Value, HashFunction, KeyEqual> {
private:
using super = Internal::UntimedCacheBase<Key, Value, HashFunction, KeyEqual>;
using PRIVATE_BASE_CACHE_MEMBERS;
public:
using PUBLIC_BASE_CACHE_MEMBERS;
using typename super::size_t;
/// \copydoc BaseCache::BaseCache(size_t,const HashFunction&,const KeyEqual&)
/// \detailss The capacity defaults to an internal constant, currently 128.
explicit Cache(size_t capacity = Internal::DEFAULT_CAPACITY,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, hash, equal) {
}
/// \copydoc BaseCache(size_t,Iterator,Iterator,const HashFunction&,const
/// KeyEqual&)
template <typename Iterator>
Cache(size_t capacity,
Iterator begin,
Iterator end,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, begin, end, hash, equal) {
}
/// \copydoc BaseCache(Iterator,Iterator,const HashFunction&,const
/// KeyEqual&)
template <typename Iterator>
Cache(Iterator begin,
Iterator end,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(begin, end, hash, equal) {
}
/// Constructor.
///
/// \param capacity The capacity of the cache.
/// \param range A range to construct the cache with.
/// \param hash The hash function to use for the internal map.
/// \param key_equal The key equality function to use for the internal map.
template <typename Range, typename = Internal::enable_if_range<Range>>
Cache(size_t capacity,
Range&& range,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, std::forward<Range>(range), hash, equal) {
}
/// Constructor.
///
/// \param range A range to construct the cache with.
/// \param hash The hash function to use for the internal map.
/// \param key_equal The key equality function to use for the internal map.
template <typename Range, typename = Internal::enable_if_range<Range>>
explicit Cache(Range&& range,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(std::forward<Range>(range), hash, equal) {
}
/// \copydoc BaseCache(InitializerList,const HashFunction&,const
/// KeyEqual&)
Cache(InitializerList list,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual()) // NOLINT(runtime/explicit)
: super(list, hash, equal) {
}
/// \copydoc BaseCache(size_t,InitializerList,const HashFunction&,const
/// KeyEqual&)
Cache(size_t capacity,
InitializerList list,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual()) // NOLINT(runtime/explicit)
: super(capacity, list, hash, equal) {
}
/// \copydoc BaseCache::find(const Key&)
UnorderedIterator find(const Key& key) override {
auto iterator = _map.find(key);
if (iterator != _map.end()) {
_register_hit(key, iterator->second.value);
_move_to_front(iterator->second.order);
_last_accessed = iterator;
} else {
_register_miss(key);
}
return {*this, iterator};
}
/// \copydoc BaseCache::find(const Key&) const
UnorderedConstIterator find(const Key& key) const override {
auto iterator = _map.find(key);
if (iterator != _map.end()) {
_register_hit(key, iterator->second.value);
_move_to_front(iterator->second.order);
_last_accessed = iterator;
} else {
_register_miss(key);
}
return {*this, iterator};
}
/// \returns The most-recently inserted element.
const Key& front() const noexcept {
if (is_empty()) {
throw LRU::Error::EmptyCache("front");
} else {
// The queue is reversed for natural order of iteration.
return _order.back();
}
}
/// \returns The least-recently inserted element.
const Key& back() const noexcept {
if (is_empty()) {
throw LRU::Error::EmptyCache("back");
} else {
// The queue is reversed for natural order of iteration.
return _order.front();
}
}
};
namespace Lowercase {
template <typename... Ts>
using cache = Cache<Ts...>;
} // namespace Lowercase
} // namespace LRU
#endif // LRU_CACHE_HPP

View File

@@ -0,0 +1,134 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_PAIR_HPP
#define LRU_PAIR_HPP
#include <algorithm>
#include <type_traits>
#include <utility>
namespace LRU {
/// A entry of references to the key and value of an entry in a cache.
///
/// Instances of this class are usually the result of dereferencing an iterator.
///
/// \tparam Key The key type of the pair.
/// \tparam Value The value type of the pair.
template <typename Key, typename Value>
struct Entry final {
using KeyType = Key;
using ValueType = Value;
using first_type = Key;
using second_type = Value;
/// Constructor.
///
/// \param key The key of the entry.
/// \param value The value of the entry.
Entry(const Key& key, Value& value) : first(key), second(value) {
}
/// Generalized copy constructor.
///
/// Mainly for conversion from non-const values to const values.
///
/// \param other The entry to construct from.
template <typename AnyKey,
typename AnyValue,
typename =
std::enable_if_t<(std::is_convertible<AnyKey, Key>::value &&
std::is_convertible<AnyValue, Value>::value)>>
Entry(const Entry<AnyKey, AnyValue>& other)
: first(other.first), second(other.second) {
}
/// Compares two entrys for equality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.
/// \returns True if the firest entry equals the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator==(const Entry& first, const Pair& second) noexcept {
return first.first == second.first && first.second == second.second;
}
/// Compares two entrys for equality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.
/// \returns True if the first entry equals the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator==(const Pair& first, const Entry& second) noexcept {
return second == first;
}
/// Compares two entrys for inequality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.
/// \returns True if the first entry does not equal the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator!=(const Entry& first, const Pair& second) noexcept {
return !(first == second);
}
/// Compares two entrys for inequality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.fdas
/// \returns True if the first entry does not equal the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator!=(const Pair& first, const Entry& second) noexcept {
return second != first;
}
/// \returns A `std::pair` instance with the key and value of this entry.
operator std::pair<const Key&, Value&>() noexcept {
return {first, second};
}
/// \returns The key of the entry (`first`).
const Key& key() const noexcept {
return first;
}
/// \returns The value of the entry (`second`).
Value& value() noexcept {
return second;
}
/// \returns The value of the entry (`second`).
const Value& value() const noexcept {
return second;
}
/// The key of the entry.
const Key& first;
/// The value of the entry.
Value& second;
};
} // namespace LRU
#endif // LRU_PAIR_HPP

View File

@@ -0,0 +1,107 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_ERRORS_HPP
#define LRU_INTERNAL_ERRORS_HPP
#include <stdexcept>
#include <string>
namespace LRU {
namespace Error {
/// Exception thrown when the value of an invalid key was requested.
struct KeyNotFound : public std::runtime_error {
using super = std::runtime_error;
KeyNotFound() : super("Failed to find key") {
}
explicit KeyNotFound(const std::string& key)
: super("Failed to find key: " + key) {
}
};
/// Exception thrown when the value of an expired key was requested.
struct KeyExpired : public std::runtime_error {
using super = std::runtime_error;
explicit KeyExpired(const std::string& key)
: super("Key found, but expired: " + key) {
}
KeyExpired() : super("Key found, but expired") {
}
};
/// Exception thrown when requesting the front or end key of an empty cache.
struct EmptyCache : public std::runtime_error {
using super = std::runtime_error;
explicit EmptyCache(const std::string& what_was_expected)
: super("Requested " + what_was_expected + " of empty cache") {
}
};
/// Exception thrown when attempting to convert an invalid unordered iterator to
/// an ordered iterator.
struct InvalidIteratorConversion : public std::runtime_error {
using super = std::runtime_error;
InvalidIteratorConversion()
: super("Cannot convert past-the-end unordered to ordered iterator") {
}
};
/// Exception thrown when attempting to erase the past-the-end iterator.
struct InvalidIterator : public std::runtime_error {
using super = std::runtime_error;
InvalidIterator() : super("Past-the-end iterator is invalid here") {
}
};
/// Exception thrown when requesting statistics about an unmonitored key.
struct UnmonitoredKey : public std::runtime_error {
using super = std::runtime_error;
UnmonitoredKey() : super("Requested statistics for unmonitored key") {
}
};
/// Exception thrown when requesting the statistics object of a cache when none
/// was registered.
struct NotMonitoring : public std::runtime_error {
using super = std::runtime_error;
NotMonitoring() : super("Statistics monitoring not enabled for this cache") {
}
};
namespace Lowercase {
using key_not_found = KeyNotFound;
using key_expired = KeyExpired;
using empty_cache = EmptyCache;
using invalid_iterator_conversion = InvalidIteratorConversion;
using invalid_iterator = InvalidIterator;
using unmonitored_key = UnmonitoredKey;
using not_monitoring = NotMonitoring;
} // namespace Lowercase
} // namespace Error
} // namespace LRU
#endif // LRU_INTERNAL_ERRORS_HPP

View File

@@ -0,0 +1,76 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INSERTION_RESULT_HPP
#define LRU_INSERTION_RESULT_HPP
#include <algorithm>
#include <type_traits>
#include <utility>
namespace LRU {
/// The result of an insertion into a cache.
///
/// This is a semantically nicer alternative to a generic `std::pair`, as is
/// returned by `std::unordered_map` or so. It still has the same static
/// interface as the `std::pair` (with `first` and `second` members), but adds
/// nicer `was_inserted()` and `iterator()` accessors.
///
/// \tparam Iterator The class of the iterator contained in the result.
template <typename Iterator>
struct InsertionResult final {
using IteratorType = Iterator;
/// Constructor.
///
/// \param result Whether the result was successful.
/// \param iterator The iterator pointing to the inserted or updated key.
InsertionResult(bool result, Iterator iterator)
: first(result), second(iterator) {
}
/// \returns True if the key was newly inserted, false if it was only updated.
bool was_inserted() const noexcept {
return first;
}
/// \returns The iterator pointing to the inserted or updated key.
Iterator iterator() const noexcept {
return second;
}
/// \copydoc was_inserted
explicit operator bool() const noexcept {
return was_inserted();
}
/// Whether the result was successful.
bool first;
/// The iterator pointing to the inserted or updated key.
Iterator second;
};
} // namespace LRU
#endif // LRU_INSERTION_RESULT_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_BASE_ITERATOR_HPP
#define LRU_INTERNAL_BASE_ITERATOR_HPP
#include <algorithm>
#include <iterator>
#include <lru/entry.hpp>
#include <lru/internal/optional.hpp>
#define PUBLIC_BASE_ITERATOR_MEMBERS \
typename super::Entry; \
using typename super::KeyType; \
using typename super::ValueType;
#define PRIVATE_BASE_ITERATOR_MEMBERS \
super::_iterator; \
using super::_entry; \
using super::_cache;
namespace LRU {
namespace Internal {
/// The base class for all (ordered and unordered) iterators.
///
/// All iterators over our LRU caches store a reference to the cache they point
/// into, an underlying iterator they adapt (e.g. a map iterator or list
/// iterator) as well as a entry, a reference to which is returned when
/// dereferencing the iterator.
///
/// \tparam IteratorTag A standard iterator category tag.
/// \tparam Key The key type over which instances of the iterator iterate.
/// \tparam Value The value type over which instances of the iterator iterate.
/// \tparam Cache The type of the cache instances of the iterator point into.
/// \tparam UnderlyingIterator The underlying iterator class used to implement
/// the iterator.
template <typename IteratorTag,
typename Key,
typename Value,
typename Cache,
typename UnderlyingIterator>
class BaseIterator : public std::iterator<IteratorTag, LRU::Entry<Key, Value>> {
public:
using KeyType = Key;
using ValueType =
std::conditional_t<std::is_const<Cache>::value, const Value, Value>;
using Entry = LRU::Entry<KeyType, ValueType>;
/// Default constructor.
BaseIterator() noexcept : _cache(nullptr) {
}
/// Constructor.
///
/// \param cache The cache this iterator points into.
/// \param iterator The underlying iterator to adapt.
BaseIterator(Cache& cache, const UnderlyingIterator& iterator) noexcept
: _iterator(iterator), _cache(&cache) {
}
/// Copy constructor.
///
/// Differs from the default copy constructor in that it does not copy the
/// entry.
///
/// \param other The base iterator to copy.
BaseIterator(const BaseIterator& other) noexcept
: _iterator(other._iterator), _cache(other._cache) {
// Note: we do not copy the entry, as it would require a new allocation.
// Since iterators are often taken by value, this may incur a high cost.
// As such we delay the retrieval of the entry to the first call to entry().
}
/// Copy assignment operator.
///
/// Differs from the default copy assignment
/// operator in that it does not copy the entry.
///
/// \param other The base iterator to copy.
/// \return The base iterator instance.
BaseIterator& operator=(const BaseIterator& other) noexcept {
if (this != &other) {
_iterator = other._iterator;
_cache = other._cache;
_entry.reset();
}
return *this;
}
/// Move constructor.
BaseIterator(BaseIterator&& other) noexcept = default;
/// Move assignment operator.
BaseIterator& operator=(BaseIterator&& other) noexcept = default;
/// Generalized copy constructor.
///
/// Mainly necessary for non-const to const conversion.
///
/// \param other The base iterator to copy from.
template <typename AnyIteratorTag,
typename AnyKeyType,
typename AnyValueType,
typename AnyCacheType,
typename AnyUnderlyingIteratorType>
BaseIterator(const BaseIterator<AnyIteratorTag,
AnyKeyType,
AnyValueType,
AnyCacheType,
AnyUnderlyingIteratorType>& other)
: _iterator(other._iterator), _entry(other._entry), _cache(other._cache) {
}
/// Generalized move constructor.
///
/// Mainly necessary for non-const to const conversion.
///
/// \param other The base iterator to move into this one.
template <typename AnyIteratorTag,
typename AnyKeyType,
typename AnyValueType,
typename AnyCacheType,
typename AnyUnderlyingIteratorType>
BaseIterator(BaseIterator<AnyIteratorTag,
AnyKeyType,
AnyValueType,
AnyCacheType,
AnyUnderlyingIteratorType>&& other) noexcept
: _iterator(std::move(other._iterator))
, _entry(std::move(other._entry))
, _cache(std::move(other._cache)) {
}
/// Destructor.
virtual ~BaseIterator() = default;
/// Swaps this base iterator with another one.
///
/// \param other The other iterator to swap with.
void swap(BaseIterator& other) noexcept {
// Enable ADL
using std::swap;
swap(_iterator, other._iterator);
swap(_entry, other._entry);
swap(_cache, other._cache);
}
/// Swaps two base iterator.
///
/// \param first The first iterator to swap.
/// \param second The second iterator to swap.
friend void swap(BaseIterator& first, BaseIterator& second) noexcept {
first.swap(second);
}
/// \returns A reference to the current entry pointed to by the iterator.
virtual Entry& operator*() noexcept = 0;
/// \returns A pointer to the current entry pointed to by the iterator.
Entry* operator->() noexcept {
return &(**this);
}
/// \copydoc operator*()
virtual Entry& entry() = 0;
/// \returns A reference to the value of the entry currently pointed to by the
/// iterator.
virtual ValueType& value() = 0;
/// \returns A reference to the key of the entry currently pointed to by the
/// iterator.
virtual const Key& key() = 0;
protected:
template <typename, typename, typename, typename, typename>
friend class BaseIterator;
/// The underlying iterator this iterator class adapts.
UnderlyingIterator _iterator;
/// The entry optionally being stored.
Optional<Entry> _entry;
/// A pointer to the cache this iterator points into.
/// Pointer and not reference because it's cheap to copy.
/// Pointer and not `std::reference_wrapper` because the class needs to be
/// default-constructible.
Cache* _cache;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_BASE_ITERATOR_HPP

View File

@@ -0,0 +1,338 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef BASE_ORDERED_ITERATOR_HPP
#define BASE_ORDERED_ITERATOR_HPP
#include <algorithm>
#include <functional>
#include <iterator>
#include <type_traits>
#include <lru/entry.hpp>
#include <lru/error.hpp>
#include <lru/internal/base-iterator.hpp>
#include <lru/internal/base-unordered-iterator.hpp>
#include <lru/internal/definitions.hpp>
#include <lru/internal/optional.hpp>
#include <lru/iterator-tags.hpp>
namespace LRU {
namespace Internal {
template <typename Key, typename Value, typename Cache>
using BaseForBaseOrderedIterator =
BaseIterator<std::bidirectional_iterator_tag,
Key,
Value,
Cache,
typename Queue<Key>::const_iterator>;
/// The base class for all const and non-const ordered iterators.
///
/// Ordered iterators are bidirectional iterators that iterate over the keys of
/// a cache in the order in which they were inserted into the cache. As they
/// only iterate over the keys, they must perform hash lookups to retrieve the
/// value the first time they are dereferenced. This makes them slightly less
/// efficient than unordered iterators. However, they also have the additional
/// property that they may be constructed from unordered iterators, and that
/// they can be decremented.
///
/// \tparam Key The key type over which instances of the iterator iterate.
/// \tparam Value The value type over which instances of the iterator iterate.
/// \tparam Cache The type of the cache instances of the iterator point into.
template <typename Key, typename Value, typename Cache>
class BaseOrderedIterator
: public BaseForBaseOrderedIterator<Key, Value, Cache> {
protected:
using super = BaseForBaseOrderedIterator<Key, Value, Cache>;
using PRIVATE_BASE_ITERATOR_MEMBERS;
using UnderlyingIterator = typename Queue<Key>::const_iterator;
public:
using Tag = LRU::Tag::OrderedIterator;
using PUBLIC_BASE_ITERATOR_MEMBERS;
/// Constructor.
BaseOrderedIterator() noexcept = default;
/// \copydoc BaseIterator::BaseIterator(Cache,UnderlyingIterator)
BaseOrderedIterator(Cache& cache, UnderlyingIterator iterator)
: super(cache, iterator) {
}
/// Generalized copy constructor.
///
/// \param other The ordered iterator to contruct from.
template <typename AnyKey, typename AnyValue, typename AnyCache>
BaseOrderedIterator(
const BaseOrderedIterator<AnyKey, AnyValue, AnyCache>& other)
: super(other) {
}
/// Generalized move constructor.
///
/// \param other The ordered iterator to move into this one.
template <typename AnyKey, typename AnyValue, typename AnyCache>
BaseOrderedIterator(BaseOrderedIterator<AnyKey, AnyValue, AnyCache>&& other)
: super(std::move(other)) {
}
/// Generalized conversion copy constructor.
///
/// \param unordered_iterator The unordered iterator to construct from.
template <
typename AnyCache,
typename UnderlyingIterator,
typename = std::enable_if_t<
std::is_same<std::decay_t<AnyCache>, std::decay_t<Cache>>::value>>
BaseOrderedIterator(const BaseUnorderedIterator<AnyCache, UnderlyingIterator>&
unordered_iterator) {
// Atomicity
_throw_if_at_invalid(unordered_iterator);
_cache = unordered_iterator._cache;
_iterator = unordered_iterator._iterator->second.order;
}
/// Generalized conversion move constructor.
///
/// \param unordered_iterator The unordered iterator to move-construct from.
template <
typename AnyCache,
typename UnderlyingIterator,
typename = std::enable_if_t<
std::is_same<std::decay_t<AnyCache>, std::decay_t<Cache>>::value>>
BaseOrderedIterator(BaseUnorderedIterator<AnyCache, UnderlyingIterator>&&
unordered_iterator) {
// Atomicity
_throw_if_at_invalid(unordered_iterator);
_cache = std::move(unordered_iterator._cache);
_entry = std::move(unordered_iterator._entry);
_iterator = std::move(unordered_iterator._iterator->second.order);
}
/// Copy constructor.
BaseOrderedIterator(const BaseOrderedIterator& other) = default;
/// Move constructor.
BaseOrderedIterator(BaseOrderedIterator&& other) = default;
/// Copy assignment operator.
BaseOrderedIterator& operator=(const BaseOrderedIterator& other) = default;
/// Move assignment operator.
BaseOrderedIterator& operator=(BaseOrderedIterator&& other) = default;
/// Destructor.
virtual ~BaseOrderedIterator() = default;
/// Checks for equality between this iterator and another ordered iterator.
///
/// \param other The other ordered iterator.
/// \returns True if both iterators point to the same entry, else false.
bool operator==(const BaseOrderedIterator& other) const noexcept {
return this->_iterator == other._iterator;
}
/// Checks for inequality between this iterator another ordered iterator.
///
/// \param other The other ordered iterator.
/// \returns True if the iterators point to different entries, else false.
bool operator!=(const BaseOrderedIterator& other) const noexcept {
return !(*this == other);
}
/// Checks for inequality between this iterator and another unordered
/// iterator.
///
/// \param other The other unordered iterator.
/// \returns True if both iterators point to the end of the same cache, else
/// the result of comparing with the unordered iterator, converted to an
/// ordered iterator.
template <typename AnyCache, typename AnyUnderlyingIterator>
bool operator==(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>& other) const
noexcept {
if (this->_cache != other._cache) return false;
// The past-the-end iterators of the same cache should compare equal.
// This is an exceptional guarantee we make. This is also the reason
// why we can't rely on the conversion from unordered to ordered iterators
// because construction of an ordered iterator from the past-the-end
// unordered iterator will fail (with an InvalidIteratorConversion error)
if (other == other._cache->unordered_end()) {
return *this == this->_cache->ordered_end();
}
// Will call the other overload
return *this == static_cast<BaseOrderedIterator>(other);
}
/// Checks for equality between an unordered iterator and an ordered iterator.
///
/// \param first The unordered iterator.
/// \param second The ordered iterator.
/// \returns True if both iterators point to the end of the same cache, else
/// the result of comparing with the unordered iterator, converted to an
/// ordered iterator.
template <typename AnyCache, typename AnyUnderlyingIterator>
friend bool operator==(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>& first,
const BaseOrderedIterator& second) noexcept {
return second == first;
}
/// Checks for inequality between an unordered
/// iterator and an ordered iterator.
///
/// \param first The ordered iterator.
/// \param second The unordered iterator.
/// \returns True if the iterators point to different entries, else false.
template <typename AnyCache, typename AnyUnderlyingIterator>
friend bool
operator!=(const BaseOrderedIterator& first,
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>&
second) noexcept {
return !(first == second);
}
/// Checks for inequality between an unordered
/// iterator and an ordered iterator.
///
/// \param first The unordered iterator.
/// \param second The ordered iterator.
/// \returns True if the iterators point to different entries, else false.
template <typename AnyCache, typename AnyUnderlyingIterator>
friend bool operator!=(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>& first,
const BaseOrderedIterator& second) noexcept {
return second != first;
}
/// Increments the iterator to the next entry.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns The resulting iterator.
BaseOrderedIterator& operator++() {
++_iterator;
_entry.reset();
return *this;
}
/// Increments the iterator and returns a copy of the previous one.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns A copy of the previous iterator.
BaseOrderedIterator operator++(int) {
auto previous = *this;
++*this;
return previous;
}
/// Decrements the iterator to the previous entry.
///
/// \returns The resulting iterator.
BaseOrderedIterator& operator--() {
--_iterator;
_entry.reset();
return *this;
}
/// Decrements the iterator and returns a copy of the previous entry.
///
/// \returns The previous iterator.
BaseOrderedIterator operator--(int) {
auto previous = *this;
--*this;
return previous;
}
Entry& operator*() noexcept override {
return _maybe_lookup();
}
/// \returns A reference to the entry the iterator points to.
/// \details If the iterator is invalid, behavior is undefined.
Entry& entry() override {
_cache->throw_if_invalid(*this);
return _maybe_lookup();
}
/// \returns A reference to the value the iterator points to.
/// \details If the iterator is invalid, behavior is undefined.
Value& value() override {
return entry().value();
}
/// \returns A reference to the key the iterator points to.
/// \details If the iterator is invalid, behavior is undefined.
const Key& key() override {
// No lookup required
_cache->throw_if_invalid(*this);
return *_iterator;
}
protected:
template <typename, typename, typename>
friend class BaseOrderedIterator;
/// Looks up the entry for a key if this was not done already.
///
/// \returns The entry, which was possibly newly looked up.
Entry& _maybe_lookup() {
if (!_entry.has_value()) {
_lookup();
}
return *_entry;
}
/// Looks up the entry for a key and sets the internal entry member.
void _lookup() {
auto iterator = _cache->_map.find(*_iterator);
_entry.emplace(iterator->first, iterator->second.value);
}
private:
/// Throws an exception if the given unordered iterator is invalid.
///
/// \param unordered_iterator The iterator to check.
/// \throws LRU::Error::InvalidIteratorConversion if the iterator is invalid.
template <typename UnorderedIterator>
void _throw_if_at_invalid(const UnorderedIterator& unordered_iterator) {
// For atomicity of the copy assignment, we assign the cache pointer only
// after this check in the copy/move constructor and use the iterator's
// cache. If an exception is thrown, the state of the ordered iterator is
// unchanged compared to before the assignment.
if (unordered_iterator == unordered_iterator._cache->unordered_end()) {
throw LRU::Error::InvalidIteratorConversion();
}
}
};
} // namespace Internal
} // namespace LRU
#endif // BASE_ORDERED_ITERATOR_HPP

View File

@@ -0,0 +1,216 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef BASE_UNORDERED_ITERATOR_HPP
#define BASE_UNORDERED_ITERATOR_HPP
#include <algorithm>
#include <iterator>
#include <type_traits>
#include <lru/entry.hpp>
#include <lru/internal/base-iterator.hpp>
#include <lru/internal/definitions.hpp>
#include <lru/internal/optional.hpp>
#include <lru/iterator-tags.hpp>
namespace LRU {
// Forward declaration.
template <typename, typename, typename, typename, typename>
class TimedCache;
namespace Internal {
template <typename Cache, typename UnderlyingIterator>
using BaseForBaseUnorderedIterator =
BaseIterator<std::forward_iterator_tag,
decltype(UnderlyingIterator()->first),
decltype(UnderlyingIterator()->second.value),
Cache,
UnderlyingIterator>;
/// The base class for all const and non-const unordered iterators.
///
/// An unordered iterator is a wrapper around an `unordered_map` iterator with
/// ForwardIterator category. As such, it is (nearly) as fast to access the pair
/// as through the unordered iterator as through the map iterator directly.
/// However, the order of keys is unspecified. For this reason, unordered
/// iterators have the special property that they may be used to construct
/// ordered iterators, after which the order of insertion is respected.
///
/// \tparam Cache The type of the cache instances of the iterator point into.
/// \tparam UnderlyingIterator The underlying iterator class used to implement
/// the iterator.
template <typename Cache, typename UnderlyingIterator>
class BaseUnorderedIterator
: public BaseForBaseUnorderedIterator<Cache, UnderlyingIterator> {
protected:
using super = BaseForBaseUnorderedIterator<Cache, UnderlyingIterator>;
using PRIVATE_BASE_ITERATOR_MEMBERS;
// These are the key and value types the BaseIterator extracts
using Key = typename super::KeyType;
using Value = typename super::ValueType;
public:
using Tag = LRU::Tag::UnorderedIterator;
using PUBLIC_BASE_ITERATOR_MEMBERS;
/// Constructor.
BaseUnorderedIterator() noexcept = default;
/// \copydoc BaseIterator::BaseIterator(Cache,UnderlyingIterator)
explicit BaseUnorderedIterator(Cache& cache,
const UnderlyingIterator& iterator) noexcept
: super(cache, iterator) {
}
/// Generalized copy constructor.
///
/// Useful mainly for non-const to const conversion.
///
/// \param other The iterator to copy from.
template <typename AnyCache, typename AnyUnderlyingIterator>
BaseUnorderedIterator(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>&
other) noexcept
: super(other) {
}
/// Copy constructor.
BaseUnorderedIterator(const BaseUnorderedIterator& other) noexcept = default;
/// Move constructor.
BaseUnorderedIterator(BaseUnorderedIterator&& other) noexcept = default;
/// Copy assignment operator.
BaseUnorderedIterator&
operator=(const BaseUnorderedIterator& other) noexcept = default;
/// Move assignment operator.
template <typename AnyCache, typename AnyUnderlyingIterator>
BaseUnorderedIterator&
operator=(BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>
unordered_iterator) noexcept {
swap(unordered_iterator);
return *this;
}
/// Destructor.
virtual ~BaseUnorderedIterator() = default;
/// Compares this iterator for equality with another unordered iterator.
///
/// \param other Another unordered iterator.
/// \returns True if both iterators point to the same entry, else false.
template <typename AnyCache, typename AnyIterator>
bool
operator==(const BaseUnorderedIterator<AnyCache, AnyIterator>& other) const
noexcept {
return this->_iterator == other._iterator;
}
/// Compares this iterator for inequality with another unordered iterator.
///
/// \param other Another unordered iterator.
/// \returns True if the iterators point to different entries, else false.
template <typename AnyCache, typename AnyIterator>
bool
operator!=(const BaseUnorderedIterator<AnyCache, AnyIterator>& other) const
noexcept {
return !(*this == other);
}
/// Increments the iterator to the next entry.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns The resulting iterator.
BaseUnorderedIterator& operator++() {
++_iterator;
_entry.reset();
return *this;
}
/// Increments the iterator and returns a copy of the previous one.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns A copy of the previous iterator.
BaseUnorderedIterator operator++(int) {
auto previous = *this;
++*this;
return previous;
}
/// \copydoc BaseIterator::operator*
/// \details If the iterator is invalid, behavior is undefined. No exception
/// handling is performed.
Entry& operator*() noexcept override {
if (!_entry.has_value()) {
_entry.emplace(_iterator->first, _iterator->second.value);
}
return *_entry;
}
/// \returns A reference to the entry the iterator points to.
/// \throws LRU::Error::InvalidIterator if the iterator is the end iterator.
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
Entry& entry() override {
if (!_entry.has_value()) {
_entry.emplace(_iterator->first, _iterator->second.value);
}
_cache->throw_if_invalid(*this);
return *_entry;
}
/// \returns A reference to the key the iterator points to.
/// \throws LRU::Error::InvalidIterator if the iterator is the end iterator.
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
const Key& key() override {
return entry().key();
}
/// \returns A reference to the value the iterator points to.
/// \throws LRU::Error::InvalidIterator if the iterator is the end iterator.
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
Value& value() override {
return entry().value();
}
protected:
template <typename, typename, typename>
friend class BaseOrderedIterator;
template <typename, typename, typename, typename, typename>
friend class LRU::TimedCache;
};
} // namespace Internal
} // namespace LRU
#endif // BASE_UNORDERED_ITERATOR_HPP

View File

@@ -0,0 +1,159 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_CALLBACK_MANAGER_HPP
#define LRU_INTERNAL_CALLBACK_MANAGER_HPP
#include <functional>
#include <vector>
#include <lru/entry.hpp>
#include <lru/internal/optional.hpp>
namespace LRU {
namespace Internal {
/// Manages hit, miss and access callbacks for a cache.
///
/// The callback manager implements the "publisher" of the observer pattern we
/// implement. It stores and calls three kinds of callbacks:
/// 1. Hit callbacks, taking a key and value after a cache hit.
/// 2. Miss callbacks, taking only a key, that was not found in a cache.
/// 3. Access callbacks, taking a key and a boolean indicating a hit or a miss.
///
/// Callbacks can be added, accessed and cleared.
template <typename Key, typename Value>
class CallbackManager {
public:
using HitCallback = std::function<void(const Key&, const Value&)>;
using MissCallback = std::function<void(const Key&)>;
using AccessCallback = std::function<void(const Key&, bool)>;
using HitCallbackContainer = std::vector<HitCallback>;
using MissCallbackContainer = std::vector<MissCallback>;
using AccessCallbackContainer = std::vector<AccessCallback>;
/// Calls all callbacks registered for a hit, with the given key and value.
///
/// \param key The key for which a cache hit ocurred.
/// \param value The value that was found for the key.
void hit(const Key& key, const Value& value) {
_call_each(_hit_callbacks, key, value);
_call_each(_access_callbacks, key, true);
}
/// Calls all callbacks registered for a miss, with the given key.
///
/// \param key The key for which a cache miss ocurred.
void miss(const Key& key) {
_call_each(_miss_callbacks, key);
_call_each(_access_callbacks, key, false);
}
/// Registers a new hit callback.
///
/// \param hit_callback The hit callback function to register with the
/// manager.
template <typename Callback>
void hit_callback(Callback&& hit_callback) {
_hit_callbacks.emplace_back(std::forward<Callback>(hit_callback));
}
/// Registers a new miss callback.
///
/// \param miss_callback The miss callback function to register with the
/// manager.
template <typename Callback>
void miss_callback(Callback&& miss_callback) {
_miss_callbacks.emplace_back(std::forward<Callback>(miss_callback));
}
/// Registers a new access callback.
///
/// \param access_callback The access callback function to register with the
/// manager.
template <typename Callback>
void access_callback(Callback&& access_callback) {
_access_callbacks.emplace_back(std::forward<Callback>(access_callback));
}
/// Clears all hit callbacks.
void clear_hit_callbacks() {
_hit_callbacks.clear();
}
/// Clears all miss callbacks.
void clear_miss_callbacks() {
_miss_callbacks.clear();
}
/// Clears all access callbacks.
void clear_access_callbacks() {
_access_callbacks.clear();
}
/// Clears all callbacks.
void clear() {
clear_hit_callbacks();
clear_miss_callbacks();
clear_access_callbacks();
}
/// \returns All hit callbacks.
const HitCallbackContainer& hit_callbacks() const noexcept {
return _hit_callbacks;
}
/// \returns All miss callbacks.
const MissCallbackContainer& miss_callbacks() const noexcept {
return _miss_callbacks;
}
/// \returns All access callbacks.
const AccessCallbackContainer& access_callbacks() const noexcept {
return _access_callbacks;
}
private:
/// Calls each function in the given container with the given arguments.
///
/// \param callbacks The container of callbacks to call.
/// \param args The arguments to call the callbacks with.
template <typename CallbackContainer, typename... Args>
void _call_each(const CallbackContainer& callbacks, Args&&... args) {
for (const auto& callback : callbacks) {
callback(std::forward<Args>(args)...);
}
}
/// The container of hit callbacks registered.
HitCallbackContainer _hit_callbacks;
/// The container of miss callbacks registered.
MissCallbackContainer _miss_callbacks;
/// The container of access callbacks registered.
AccessCallbackContainer _access_callbacks;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_CALLBACK_MANAGER_HPP

View File

@@ -0,0 +1,88 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_DEFINITIONS_HPP
#define LRU_INTERNAL_DEFINITIONS_HPP
#include <chrono>
#include <cstddef>
#include <functional>
#include <list>
#include <tuple>
#include <unordered_map>
namespace LRU {
namespace Internal {
/// The default capacity for all caches.
const std::size_t DEFAULT_CAPACITY = 128;
/// The reference type use to store keys in the order queue.
template <typename T>
using Reference = std::reference_wrapper<T>;
/// Compares two References for equality.
///
/// This is necessary because `std::reference_wrapper` does not define any
/// operator overloads. We do need them, however (e.g. for container
/// comparison).
///
/// \param first The first reference to compare.
/// \param second The second reference to compare.
template <typename T, typename U>
bool operator==(const Reference<T>& first, const Reference<U>& second) {
return first.get() == second.get();
}
/// Compares two References for inequality.
///
/// This is necessary because `std::reference_wrapper` does not define any
/// operator overloads. We do need them, however (e.g. for container
/// comparison).
///
/// \param first The first reference to compare.
/// \param second The second reference to compare.
template <typename T, typename U>
bool operator!=(const Reference<T>& first, const Reference<U>& second) {
return !(first == second);
}
/// The default queue type used internally.
template <typename T>
using Queue = std::list<Reference<T>>;
/// The default map type used internally.
template <typename Key,
typename Information,
typename HashFunction,
typename KeyEqual>
using Map = std::unordered_map<Key, Information, HashFunction, KeyEqual>;
/// The default clock used internally.
using Clock = std::chrono::steady_clock;
/// The default timestamp (time point) used internally.
using Timestamp = Clock::time_point;
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_DEFINITIONS_HPP

View File

@@ -0,0 +1,62 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_HASH_HPP
#define LRU_INTERNAL_HASH_HPP
#include <cstddef>
#include <functional>
#include <tuple>
/// `std::hash` specialization to allow storing tuples as keys
/// in `std::unordered_map`.
///
/// Essentially hashes all tuple elements and jumbles the
/// individual hashes together.
namespace std {
template <typename... Ts>
struct hash<std::tuple<Ts...>> {
using argument_type = std::tuple<Ts...>;
using result_type = std::size_t;
result_type operator()(const argument_type& argument) const {
return hash_tuple(argument, std::make_index_sequence<sizeof...(Ts)>());
}
private:
template <std::size_t I, std::size_t... Is>
result_type
hash_tuple(const argument_type& tuple, std::index_sequence<I, Is...>) const {
auto value = std::get<I>(tuple);
auto current = std::hash<decltype(value)>{}(value);
auto seed = hash_tuple(tuple, std::index_sequence<Is...>());
// http://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html
return current + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
result_type hash_tuple(const argument_type&, std::index_sequence<>) const {
return 0;
}
};
} // namespace std
#endif // LRU_INTERNAL_HASH_HPP

View File

@@ -0,0 +1,145 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_INFORMATION_HPP
#define LRU_INTERNAL_INFORMATION_HPP
#include <cstddef>
#include <tuple>
#include <utility>
#include <lru/internal/definitions.hpp>
#include <lru/internal/utility.hpp>
namespace LRU {
namespace Internal {
/// The value type of internal maps, used to store a value and iterator.
///
/// This information object is the basis of an LRU cache, which must associated
/// a value and such an order iterator with a key, such that the iterator may be
/// moved to the front of the order when the key is updated with a new value.
///
/// \tparam Key The key type of the information.
/// \tparam Value The value type of the information.
template <typename Key, typename Value>
struct Information {
using KeyType = Key;
using ValueType = Value;
using QueueIterator = typename Internal::Queue<const Key>::const_iterator;
/// Constructor.
///
/// \param value_ The value for the information.
/// \param order_ The order iterator for the information.
explicit Information(const Value& value_,
QueueIterator order_ = QueueIterator())
: value(value_), order(order_) {
}
/// Constructor.
///
/// \param order_ The order iterator for the information.
/// \param value_arguments Any number of arguments to perfectly forward to the
/// value type's constructor.
// template <typename... ValueArguments>
// Information(QueueIterator order_, ValueArguments&&... value_arguments)
// : value(std::forward<ValueArguments>(value_arguments)...), order(order_) {
// }
/// Constructor.
///
/// \param order_ The order iterator for the information.
/// \param value_arguments A tuple of arguments to perfectly forward to the
/// value type's constructor.
///
template <typename... ValueArguments>
explicit Information(const std::tuple<ValueArguments...>& value_arguments,
QueueIterator order_ = QueueIterator())
: Information(
order_, value_arguments, Internal::tuple_indices(value_arguments)) {
}
/// Copy constructor.
Information(const Information& other) = default;
/// Move constructor.
Information(Information&& other) = default;
/// Copy assignment operator.
Information& operator=(const Information& other) = default;
/// Move assignment operator.
Information& operator=(Information&& other) = default;
/// Destructor.
virtual ~Information() = default;
/// Compares the information for equality with another information object.
///
/// \param other The other information object to compare to.
/// \returns True if key and value (not the iterator itself) of the two
/// information objects are equal, else false.
virtual bool operator==(const Information& other) const noexcept {
if (this == &other) return true;
if (this->value != other.value) return false;
// We do not compare the iterator (because otherwise two containers
// holding information would never be equal). We also do not compare
// the key stored in the iterator, because keys will always have been
// compared before this operator is called.
return true;
}
/// Compares the information for inequality with another information object.
///
/// \param other The other information object to compare for.
/// \returns True if key and value (not the iterator itself) of the two
/// information objects are unequal, else false.
virtual bool operator!=(const Information& other) const noexcept {
return !(*this == other);
}
/// The value of the information.
Value value;
/// The order iterator of the information.
QueueIterator order;
private:
/// Implementation for the constructor taking a tuple of arguments for the
/// value.
///
/// \param order_ The order iterator for the information.
/// \param value_argument The tuple of arguments to perfectly forward to the
/// value type's constructor.
/// \param _ An index sequence to access the elements of the tuple
template <typename... ValueArguments, std::size_t... Indices>
Information(const QueueIterator& order_,
const std::tuple<ValueArguments...>& value_argument,
std::index_sequence<Indices...> _)
: value(std::forward<ValueArguments>(std::get<Indices>(value_argument))...)
, order(order_) {
}
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_INFORMATION_HPP

View File

@@ -0,0 +1,254 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_LAST_ACCESSED_HPP
#define LRU_INTERNAL_LAST_ACCESSED_HPP
#include <algorithm>
#include <functional>
#include <iterator>
#include <lru/internal/utility.hpp>
namespace LRU {
namespace Internal {
/// Provides a simple iterator-compatible pointer object for a key and
/// information.
///
/// The easisest idea for this class, theoretically, would be to just store an s
/// iterator to the internal cache map (i.e. template the class on the iterator
/// type). However, the major trouble with that approach is that this class
/// should be 100% *mutable*, as in "always non-const", so that keys and
/// informations
/// we store for fast access can be (quickly) retrieved as either const or
/// non-const (iterators for example). This is not possible, since the
/// const-ness of `const_iterators` are not the usual idea of const in C++,
/// meaning especially it cannot be cast away with a `const_cast` as is required
/// for the mutability. As such, we *must* store the plain keys and
/// informations.
/// This, however, means that iterators cannot be stored efficiently, since a
/// new hash table lookup would be required to go from a key to its iterator.
/// However, since the main use case of this class is to avoid a second lookup
/// in the usual `if (cache.contains(key)) return cache.lookup(key)`, which is
/// not an issue for iterators since they can be compared to the `end` iterator
/// in constant time (equivalent to the call to `contains()`).
///
/// WARNING: This class stores *pointers* to keys and informations. As such
/// lifetime
/// of the pointed-to objects must be cared for by the user of this class.
///
/// \tparam Key The type of key being accessed.
/// \tparam InformationType The type of information being accessed.
/// \tparam KeyEqual The type of the key comparison function.
template <typename Key,
typename InformationType,
typename KeyEqual = std::equal_to<Key>>
class LastAccessed {
public:
/// Constructor.
///
/// \param key_equal The function to compare keys with.
explicit LastAccessed(const KeyEqual& key_equal = KeyEqual())
: _key(nullptr)
, _information(nullptr)
, _is_valid(false)
, _key_equal(key_equal) {
}
/// Constructor.
///
/// \param key The key to store a reference to.
/// \param information The information to store a reference to.
/// \param key_equal The function to compare keys with.
LastAccessed(const Key& key,
const InformationType& information,
const KeyEqual& key_equal = KeyEqual())
: _key(const_cast<Key*>(&key))
, _information(const_cast<InformationType*>(&information))
, _is_valid(true)
, _key_equal(key_equal) {
}
/// Constructor.
///
/// \param iterator An iterator pointing to a key and information to use for
/// constructing the instance.
/// \param key_equal The function to compare keys with.
template <typename Iterator>
explicit LastAccessed(Iterator iterator,
const KeyEqual& key_equal = KeyEqual())
: LastAccessed(iterator->first, iterator->second, key_equal) {
}
/// Copy assignment operator for iterators.
///
/// \param iterator An iterator pointing to a key and value to use for the
/// `LastAccessed` instance.
/// \return The resulting `LastAccessed` instance.
template <typename Iterator>
LastAccessed& operator=(Iterator iterator) {
_key = const_cast<Key*>(&(iterator->first));
_information = const_cast<InformationType*>(&(iterator->second));
_is_valid = true;
return *this;
}
/// Compares a `LastAccessed` object for equality with a key.
///
/// \param last_accessed The `LastAccessed` instance to compare.
/// \param key The key instance to compare.
/// \returns True if the key of the `LastAccessed` object's key equals the
/// given key, else false.
friend bool
operator==(const LastAccessed& last_accessed, const Key& key) noexcept {
if (!last_accessed._is_valid) return false;
return last_accessed._key_equal(key, last_accessed.key());
}
/// \copydoc operator==(const LastAccessed&,const Key&)
friend bool
operator==(const Key& key, const LastAccessed& last_accessed) noexcept {
return last_accessed == key;
}
/// Compares a `LastAccessed` object for equality with an iterator.
///
/// \param last_accessed The `LastAccessed` instance to compare.
/// \param iterator The iterator to compare with.
/// \returns True if the `LastAccessed` object's key equals that of the
/// iterator, else false.
template <typename Iterator, typename = enable_if_iterator<Iterator>>
friend bool
operator==(const LastAccessed& last_accessed, Iterator iterator) noexcept {
/// Fast comparisons to an iterator (not relying on implicit conversion)
return last_accessed == iterator->first;
}
/// \copydoc operator==(const LastAccessed&,Iterator)
template <typename Iterator, typename = enable_if_iterator<Iterator>>
friend bool
operator==(Iterator iterator, const LastAccessed& last_accessed) noexcept {
return last_accessed == iterator;
}
/// Compares a `LastAccessed` object for inequality with something.
///
/// \param last_accessed The `LastAccessed` instance to compare.
/// \param other Something else to compare to.
/// \returns True if the key of the `LastAccessed` object's key does not equal
/// the given other object's key, else false.
template <typename T>
friend bool
operator!=(const LastAccessed& last_accessed, const T& other) noexcept {
return !(last_accessed == other);
}
/// \copydoc operator!=(const LastAccessed&,const T&)
template <typename T>
friend bool
operator!=(const T& other, const LastAccessed& last_accessed) noexcept {
return !(other == last_accessed);
}
/// \returns The last accessed key.
Key& key() noexcept {
assert(is_valid());
return *_key;
}
/// \returns The last accessed key.
const Key& key() const noexcept {
assert(is_valid());
return *_key;
}
/// \returns The last accessed information.
InformationType& information() noexcept {
assert(is_valid());
return *_information;
}
/// \returns The last accessed information.
const InformationType& information() const noexcept {
assert(is_valid());
return *_information;
}
/// \returns The last accessed information.
auto& iterator() noexcept {
assert(is_valid());
return _information->order;
}
/// \returns The last accessed value.
auto& value() noexcept {
assert(is_valid());
return _information->value;
}
/// \returns The last accessed value.
const auto& value() const noexcept {
assert(is_valid());
return _information->value;
}
/// \returns True if the key and information of the instance may be accessed,
/// else false.
bool is_valid() const noexcept {
return _is_valid;
}
/// \copydoc is_valid()
explicit operator bool() const noexcept {
return is_valid();
}
/// Invalidates the instance.
void invalidate() noexcept {
_is_valid = false;
_key = nullptr;
_information = nullptr;
}
/// \returns The key comparison function used.
const KeyEqual& key_equal() const noexcept {
return _key_equal;
}
private:
/// A pointer to the key that was last accessed (if any).
Key* _key;
/// A pointer to the information that was last accessed (if any).
InformationType* _information;
/// True if the key and information pointers are valid, else false.
bool _is_valid;
/// The function used to compare keys.
KeyEqual _key_equal;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_LAST_ACCESSED_HPP

View File

@@ -0,0 +1,207 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_OPTIONAL_HPP
#define LRU_INTERNAL_OPTIONAL_HPP
#ifndef __has_include
#define USE_LRU_OPTIONAL
#elif __has_include(<optional>)
#include <optional>
namespace LRU {
namespace Internal {
template <typename T>
using Optional = std::optional<T>;
} // namespace Internal
} // namespace LRU
#else
#define USE_LRU_OPTIONAL
#endif
#ifdef USE_LRU_OPTIONAL
#include <memory>
#include <stdexcept>
namespace LRU {
namespace Internal {
// A roll-your-own replacement of `std::optional`.
//
// This class is only to be used if `std::optional` is unavailable. It
// implements an optional type simply on top of a `unique_ptr`. It is
// API-compatible with `std::optional`, as required for our purposes.
template <typename T>
class Optional {
public:
/// Constructor.
Optional() = default;
/// Copy constructor.
///
/// \param other The other optional object to copy from.
Optional(const Optional& other) {
if (other) emplace(*other);
}
/// Generalized copy constructor.
///
/// \param other The other optional object to copy from.
template <typename U,
typename = std::enable_if_t<std::is_convertible<T, U>::value>>
Optional(const Optional<U>& other) {
if (other) emplace(*other);
}
/// Move constructor.
///
/// \param other The other optional object to move into this one.
Optional(Optional&& other) noexcept {
swap(other);
}
/// Generalized move constructor.
///
/// \param other The other optional object to move into this one.
template <typename U,
typename = std::enable_if_t<std::is_convertible<T, U>::value>>
Optional(Optional<U>&& other) noexcept {
if (other) {
_value = std::make_unique<T>(std::move(*other));
}
}
/// Assignment operator.
///
/// \param other The other object to assign from.
/// \returns The resulting optional instance.
Optional& operator=(Optional other) noexcept {
swap(other);
return *this;
}
/// Swaps the contents of this optional with another one.
///
/// \param other The other optional to swap with.
void swap(Optional& other) {
_value.swap(other._value);
}
/// Swaps the contents of two optionals.
///
/// \param first The first optional to swap.
/// \param second The second optional to swap.
friend void swap(Optional& first, Optional& second) /* NOLINT */ {
first.swap(second);
}
/// \returns True if the `Optional` has a value, else false.
bool has_value() const noexcept {
return static_cast<bool>(_value);
}
/// \copydoc has_value()
explicit operator bool() const noexcept {
return has_value();
}
/// \returns A pointer to the current value. Behavior is undefined if the
/// optional has no value.
T* operator->() {
return _value.get();
}
/// \returns A const pointer to the current value. Behavior is undefined if
/// the `Optional` has no value.
const T* operator->() const {
return _value.get();
}
/// \returns A const reference to the current value. Behavior is undefined if
/// the `Optional` has no value.
const T& operator*() const {
return *_value;
}
/// \returns A reference to the current value. Behavior is undefined if
/// the `Optional` has no value.
T& operator*() {
return *_value;
}
/// \returns A reference to the current value.
/// \throws std::runtime_error If the `Optional` currently has no value.
T& value() {
if (!has_value()) {
// Actually std::bad_optional_access
throw std::runtime_error("optional has no value");
}
return *_value;
}
/// \returns A const reference to the current value.
/// \throws std::runtime_error If the `Optional` currently has no value.
const T& value() const {
if (!has_value()) {
// Actually std::bad_optional_access
throw std::runtime_error("optional has no value");
}
return *_value;
}
/// \returns The current value, or the given argument if there is no value.
/// \param default_value The value to return if this `Optional` currently has
/// no value.
template <class U>
T value_or(U&& default_value) const {
return *this ? **this : static_cast<T>(std::forward<U>(default_value));
}
/// Resets the `Optional` to have no value.
void reset() noexcept {
_value.reset();
}
/// Constructs the `Optional`'s value with the given arguments.
///
/// \param args Arguments to perfeclty forward to the value's constructor.
template <typename... Args>
void emplace(Args&&... args) {
_value = std::make_unique<T>(std::forward<Args>(args)...);
}
private:
template <typename>
friend class Optional;
/// The value, as we implement it.
std::unique_ptr<T> _value;
};
} // namespace Internal
} // namespace LRU
#endif
#endif // LRU_INTERNAL_OPTIONAL_HPP

View File

@@ -0,0 +1,150 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_STATISTICS_MUTATOR_HPP
#define LRU_STATISTICS_MUTATOR_HPP
#include <cassert>
#include <cstddef>
#include <memory>
#include <utility>
#include <lru/internal/optional.hpp>
#include <lru/statistics.hpp>
namespace LRU {
namespace Internal {
/// A mutable proxy interface to a statistics object.
///
/// The `StatisticsMutator` allows modification of the members of a statistics
/// object via a narrow interface, available only to internal classes. The point
/// of this is that while we don't want the user to be able to modify the hit or
/// miss count on a statistics object (it is "getter-only" in that sense), it's
/// also not ideal, from an encapsulation standpoint, to make the cache classes
/// (which do need to access and modify the hit and miss counts) friends of the
/// statistics. This is especially true since the caches should only need to
/// register hits or misses and not have to increment the count of total
/// accesses. As such, we really require a "package-level" interface that is not
/// visible to the end user, while at the same time providing an interface to
/// internal classes. The `StatisticsMutator` is a proxy/adapter class that
/// serves exactly this purpose. It is friends with the `Statistics` and can
/// thus access its members. At the same time the interface it defines is narrow
/// and provides only the necessary interface for the cache classes to register
/// hits and misses.
template <typename Key>
class StatisticsMutator {
public:
using StatisticsPointer = std::shared_ptr<Statistics<Key>>;
/// Constructor.
StatisticsMutator() noexcept = default;
/// Constructor.
///
/// \param stats A shared pointer lvalue reference.
StatisticsMutator(const StatisticsPointer& stats) // NOLINT(runtime/explicit)
: _stats(stats) {
}
/// Constructor.
///
/// \param stats A shared pointer rvalue reference to move into the
/// mutator.
StatisticsMutator(StatisticsPointer&& stats) // NOLINT(runtime/explicit)
: _stats(std::move(stats)) {
}
/// Registers a hit for the given key with the internal statistics.
///
/// \param key The key to register a hit for.
void register_hit(const Key& key) {
assert(has_stats());
_stats->_total_accesses += 1;
_stats->_total_hits += 1;
auto iterator = _stats->_key_map.find(key);
if (iterator != _stats->_key_map.end()) {
iterator->second.hits += 1;
}
}
/// Registers a miss for the given key with the internal statistics.
///
/// \param key The key to register a miss for.
void register_miss(const Key& key) {
assert(has_stats());
_stats->_total_accesses += 1;
auto iterator = _stats->_key_map.find(key);
if (iterator != _stats->_key_map.end()) {
iterator->second.misses += 1;
}
}
/// \returns A reference to the statistics object.
Statistics<Key>& get() noexcept {
assert(has_stats());
return *_stats;
}
/// \returns A const reference to the statistics object.
const Statistics<Key>& get() const noexcept {
assert(has_stats());
return *_stats;
}
/// \returns A `shared_ptr` to the statistics object.
StatisticsPointer& shared() noexcept {
return _stats;
}
/// \returns A const `shared_ptr` to the statistics object.
const StatisticsPointer& shared() const noexcept {
return _stats;
}
/// \returns True if the mutator has a statistics object, else false.
bool has_stats() const noexcept {
return _stats != nullptr;
}
/// \copydoc has_stats()
explicit operator bool() const noexcept {
return has_stats();
}
/// Resets the internal statistics pointer.
void reset() {
_stats.reset();
}
private:
/// A shared pointer to a statistics object.
std::shared_ptr<Statistics<Key>> _stats;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_STATISTICS_MUTATOR_HPP

View File

@@ -0,0 +1,116 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_TIMED_INFORMATION_HPP
#define LRU_INTERNAL_TIMED_INFORMATION_HPP
#include <cstddef>
#include <tuple>
#include <utility>
#include <lru/internal/definitions.hpp>
#include <lru/internal/information.hpp>
#include <lru/internal/utility.hpp>
namespace LRU {
namespace Internal {
/// The information object for timed caches.
///
/// TimedInformation differs from plain information only in that it stores the
/// creation time, to know when a key has expired.
///
/// \tparam Key The key type of the information.
/// \tparam Value The value type of the information.
template <typename Key, typename Value>
struct TimedInformation : public Information<Key, Value> {
using super = Information<Key, Value>;
using typename super::QueueIterator;
using Timestamp = Internal::Timestamp;
/// Constructor.
///
/// \param value_ The value for the information.
/// \param insertion_time_ The insertion timestamp of the key.
/// \param order_ The order iterator for the information.
TimedInformation(const Value& value_,
const Timestamp& insertion_time_,
QueueIterator order_ = QueueIterator())
: super(value_, order_), insertion_time(insertion_time_) {
}
/// Constructor.
///
/// Uses the current time as the insertion timestamp.
///
/// \param value_ The value for the information.
/// \param order_ The order iterator for the information.
explicit TimedInformation(const Value& value_,
QueueIterator order_ = QueueIterator())
: TimedInformation(value_, Internal::Clock::now(), order_) {
}
/// \copydoc Information::Information(QueueIterator,ValueArguments&&)
template <typename... ValueArguments>
TimedInformation(QueueIterator order_, ValueArguments&&... value_argument)
: super(std::forward<ValueArguments>(value_argument)..., order_)
, insertion_time(Internal::Clock::now()) {
}
/// \copydoc Information::Information(QueueIterator,const
/// std::tuple<ValueArguments...>&)
template <typename... ValueArguments>
explicit TimedInformation(
const std::tuple<ValueArguments...>& value_arguments,
QueueIterator order_ = QueueIterator())
: super(value_arguments, order_), insertion_time(Internal::Clock::now()) {
}
/// Compares this timed information for equality with another one.
///
/// Additionally to key and value equality, the timed information requires
/// that the insertion timestamps be equal.
///
/// \param other The other timed information.
/// \returns True if this information equals the other one, else false.
bool operator==(const TimedInformation& other) const noexcept {
if (super::operator!=(other)) return false;
return this->insertion_time == other.insertion_time;
}
/// Compares this timed information for inequality with another one.
///
/// \param other The other timed information.
/// \returns True if this information does not equal the other one, else
/// false.
/// \see operator==()
bool operator!=(const TimedInformation& other) const noexcept {
return !(*this == other);
}
/// The time at which the key of the information was insterted into a cache.
const Timestamp insertion_time;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_TIMED_INFORMATION_HPP

View File

@@ -0,0 +1,178 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_UTILITY_HPP
#define LRU_UTILITY_HPP
#include <cstddef>
#include <iterator>
#include <tuple>
#include <utility>
namespace LRU {
namespace Internal {
/// Generates an index sequence for a tuple.
///
/// \tparam Ts The types of the tuple (to deduce the size).
template <typename... Ts>
constexpr auto tuple_indices(const std::tuple<Ts...>&) {
return std::make_index_sequence<sizeof...(Ts)>();
}
/// Applies (in the functional sense) a tuple to the constructor of a class.
///
/// \tparam T The type to construct.
/// \tparam Indices The indices into the tuple (generated from an index
/// sequence).
/// \param args The tuple of arguments to construct the object with.
template <typename T, typename... Args, std::size_t... Indices>
constexpr T construct_from_tuple(const std::tuple<Args...>& arguments,
std::index_sequence<Indices...>) {
return T(std::forward<Args>(std::get<Indices>(arguments))...);
}
/// Applies (in the functional sense) a tuple to the constructor of a class.
///
/// \tparam T The type to construct.
/// \param args The tuple of arguments to construct the object with.
template <typename T, typename... Args>
constexpr T construct_from_tuple(const std::tuple<Args...>& args) {
return construct_from_tuple<T>(args, tuple_indices(args));
}
/// Applies (in the functional sense) a tuple to the constructor of a class.
///
/// \tparam T The type to construct.
/// \param args The tuple of arguments to construct the object with.
template <typename T, typename... Args>
constexpr T construct_from_tuple(std::tuple<Args...>&& args) {
return construct_from_tuple<T>(std::move(args), tuple_indices(args));
}
/// A type trait that disables a template overload if a type is not an iterator.
///
/// \tparam T the type to check.
template <typename T>
using enable_if_iterator = typename std::iterator_traits<T>::value_type;
/// A type trait that disables a template overload if a type is not a range.
///
/// \tparam T the type to check.
template <typename T>
using enable_if_range = std::pair<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>;
/// A type trait that disables a template overload if a type is not an iterator
/// over a pair.
///
/// \tparam T the type to check.
template <typename T>
using enable_if_iterator_over_pair =
std::pair<typename std::iterator_traits<T>::value_type::first_type,
typename std::iterator_traits<T>::value_type::first_type>;
/// A type trait that disables a template overload if a type is not convertible
/// to a target type.
///
/// \tparam Target The type one wants to check against.
/// \tparam T The type to check.
template <typename Target, typename T>
using enable_if_same = std::enable_if_t<std::is_convertible<T, Target>::value>;
/// Base case for `static_all_of` (the neutral element of AND is true).
constexpr bool static_all_of() noexcept {
return true;
}
/// Checks if all the given parameters evaluate to true.
///
/// \param head The first expression to check.
/// \param tail The remaining expression to check.
template <typename Head, typename... Tail>
constexpr bool static_all_of(Head&& head, Tail&&... tail) {
// Replace with (ts && ...) when the time is right
return std::forward<Head>(head) && static_all_of(std::forward<Tail>(tail)...);
}
/// Base case for `static_any_of` (the neutral element of OR is false).
constexpr bool static_any_of() noexcept {
return false;
}
/// Checks if any the given parameters evaluate to true.
///
/// \param head The first expression to check.
/// \param tail The remaining expression to check.
/// \returns True if any of the given parameters evaluate to true.
template <typename Head, typename... Tail>
constexpr bool static_any_of(Head&& head, Tail&&... tail) {
// Replace with (ts || ...) when the time is right
return std::forward<Head>(head) || static_any_of(std::forward<Tail>(tail)...);
}
/// Checks if none the given parameters evaluate to true.
///
/// \param ts The expressions to check.
/// \returns True if any of the given parameters evaluate to true.
template <typename... Ts>
constexpr bool static_none_of(Ts&&... ts) {
// Replace with (!ts && ...) when the time is right
return !static_any_of(std::forward<Ts>(ts)...);
}
/// Checks if all the given types are convertible to the first type.
///
/// \tparam T the first type.
/// \tparam Ts The types to check against the first.
template <typename T, typename... Ts>
constexpr bool
all_of_type = static_all_of(std::is_convertible<Ts, T>::value...);
/// Checks if none of the given types are convertible to the first type.
///
/// \tparam T the first type.
/// \tparam Ts The types to check against the first.
template <typename T, typename... Ts>
constexpr bool
none_of_type = static_none_of(std::is_convertible<Ts, T>::value...);
/// Base case for `for_each`.
template <typename Function>
void for_each(Function) noexcept {
}
/// Calls a function for each of the given variadic arguments.
///
/// \param function The function to call for each argument.
/// \param head The first value to call the function with.
/// \param tail The remaining values to call the function with.
template <typename Function, typename Head, typename... Tail>
void for_each(Function function, Head&& head, Tail&&... tail) {
function(std::forward<Head>(head));
for_each(function, std::forward<Tail>(tail)...);
}
} // namespace Internal
} // namespace LRU
#endif // LRU_UTILITY_HPP

Some files were not shown because too many files have changed in this diff Show More